next-staticblog 0.2.0-beta.3 → 0.2.0-beta.4

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 yd-tw
3
+ Copyright (c) 2025-2026 yd-tw
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README-zh.md ADDED
@@ -0,0 +1,298 @@
1
+ # next-posts
2
+
3
+ > 載入文章並將 YAML 解析到 Next.js 中!
4
+
5
+ 一個輕量、零依賴的 TypeScript 函式庫,用於在 Next.js 中建立靜態部落格等文章頁面。它會解析包含 YAML frontmatter 的 Markdown 檔案,並提供簡潔的工具函式,方便搭配 SSG(Static Site Generation)使用。
6
+
7
+ ---
8
+
9
+ ## 功能特色
10
+
11
+ - 零執行期依賴 —— 僅使用 Node.js 內建模組
12
+ - 完整 TypeScript 支援(支援泛型 metadata)
13
+ - 支援任意目錄結構
14
+ - 同時支援 `slug` 與 `slug.md` 輸入格式
15
+ - 相容 Next.js App Router 與 Pages Router
16
+
17
+ ---
18
+
19
+ ## 安裝
20
+
21
+ 使用 npm 或你喜歡的套件管理工具。
22
+
23
+ ```bash
24
+ npm install next-posts
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 快速開始
30
+
31
+ 在專案根目錄建立 `posts/` 資料夾,並放入 Markdown 檔案:
32
+
33
+ ```
34
+ my-next-app/
35
+ └── posts/
36
+ ├── hello-world.md
37
+ └── getting-started.md
38
+ ```
39
+
40
+ 每個檔案應包含 YAML frontmatter 區塊與文章內容:
41
+
42
+ ```markdown
43
+ ---
44
+ title: Hello World
45
+ date: 2024-01-01
46
+ tags:
47
+ - blog
48
+ - intro
49
+ ---
50
+
51
+ Welcome to my blog!
52
+ ```
53
+
54
+ 接著在 Next.js 中使用:
55
+
56
+ ```ts
57
+ import { getAllPosts, getPostBySlug, getAllPostParams } from "next-posts";
58
+
59
+ interface PostMeta {
60
+ title: string;
61
+ date: string;
62
+ tags?: string[];
63
+ }
64
+
65
+ // 取得所有文章
66
+ const posts = getAllPosts<PostMeta>();
67
+
68
+ // 取得單篇文章
69
+ const post = getPostBySlug<PostMeta>("hello-world");
70
+
71
+ // 產生靜態路徑(Next.js)
72
+ const paths = getAllPostParams();
73
+ ```
74
+
75
+ ---
76
+
77
+ ## API
78
+
79
+ ### `getAllPosts<T>(directory?)`
80
+
81
+ 回傳指定目錄中的所有文章,包含解析後的 frontmatter 與內容。
82
+
83
+ ```ts
84
+ const posts = getAllPosts<PostMeta>();
85
+ // [
86
+ // { slug: 'hello-world', metadata: { title: '...' }, content: '...' },
87
+ // ...
88
+ // ]
89
+ ```
90
+
91
+ | 參數 | 型別 | 預設值 | 說明 |
92
+ | ----------- | -------- | ---------- | ----------------------------- |
93
+ | `directory` | `string` | `"posts/"` | 相對於 `process.cwd()` 的路徑 |
94
+
95
+ **回傳型別:**
96
+ `Array<{ slug: string; metadata: T; content: string }>`
97
+
98
+ ---
99
+
100
+ ### `getPostBySlug<T>(slug, directory?)`
101
+
102
+ 依據 slug 取得單篇文章。支援帶或不帶 `.md` 副檔名。
103
+
104
+ ```ts
105
+ const post = getPostBySlug<PostMeta>("hello-world");
106
+ // {
107
+ // slug: 'hello-world',
108
+ // metadata: { title: 'Hello World', ... },
109
+ // content: '...'
110
+ // }
111
+ ```
112
+
113
+ | 參數 | 型別 | 預設值 | 說明 |
114
+ | ----------- | -------- | ---------- | ----------------------------- |
115
+ | `slug` | `string` | — | 檔名(可含或不含 `.md`) |
116
+ | `directory` | `string` | `"posts/"` | 相對於 `process.cwd()` 的路徑 |
117
+
118
+ **回傳型別:**
119
+ `{ slug: string; metadata: T; content: string }`
120
+
121
+ ---
122
+
123
+ ### `getAllPostSlugs(directory?)`
124
+
125
+ 回傳指定目錄中的原始檔名(包含 `.md` 副檔名)。
126
+
127
+ ```ts
128
+ const slugs = getAllPostSlugs();
129
+ // ['hello-world.md', 'getting-started.md']
130
+ ```
131
+
132
+ | 參數 | 型別 | 預設值 | 說明 |
133
+ | ----------- | -------- | ---------- | ----------------------------- |
134
+ | `directory` | `string` | `"posts/"` | 相對於 `process.cwd()` 的路徑 |
135
+
136
+ **回傳型別:**
137
+ `string[]`
138
+
139
+ ---
140
+
141
+ ### `getAllPostParams(directory?)`
142
+
143
+ 回傳適用於 Next.js `generateStaticParams` 或 `getStaticPaths` 的 slug 參數物件,會自動移除 `.md` 副檔名。
144
+
145
+ ```ts
146
+ const params = getAllPostParams();
147
+ // [{ slug: 'hello-world' }, { slug: 'getting-started' }]
148
+ ```
149
+
150
+ | 參數 | 型別 | 預設值 | 說明 |
151
+ | ----------- | -------- | ---------- | ----------------------------- |
152
+ | `directory` | `string` | `"posts/"` | 相對於 `process.cwd()` 的路徑 |
153
+
154
+ **回傳型別:**
155
+ `Array<{ slug: string }>`
156
+
157
+ ---
158
+
159
+ ### `parseFrontmatter(input)`
160
+
161
+ 解析原始 Markdown 字串並提取 YAML frontmatter。適用於非從檔案系統讀取的 Markdown,或需要較底層控制時使用。
162
+
163
+ ```ts
164
+ import { parseFrontmatter } from "next-posts";
165
+
166
+ const raw = `---
167
+ title: Hello World
168
+ date: 2024-01-01
169
+ tags:
170
+ - blog
171
+ ---
172
+
173
+ Welcome to my blog!`;
174
+
175
+ const { data, content } = parseFrontmatter(raw);
176
+ ```
177
+
178
+ - `data`:解析後的 YAML frontmatter 物件,若無有效 frontmatter 則為 `{}`。
179
+ - `content`:移除 frontmatter 後的 Markdown 內容。
180
+
181
+ | 參數 | 型別 | 說明 |
182
+ | ------- | -------- | -------------------------------- |
183
+ | `input` | `string` | 含有 frontmatter 的原始 Markdown |
184
+
185
+ **回傳型別:**
186
+ `{ data: Record<string, unknown>; content: string }`
187
+
188
+ ---
189
+
190
+ ## 搭配 Next.js 使用
191
+
192
+ ### App Router
193
+
194
+ ```ts
195
+ // app/blog/[slug]/page.tsx
196
+ import { getAllPostParams, getPostBySlug } from 'next-posts';
197
+
198
+ interface PostMeta {
199
+ title: string;
200
+ date: string;
201
+ }
202
+
203
+ export function generateStaticParams() {
204
+ return getAllPostParams();
205
+ }
206
+
207
+ export default function BlogPost({ params }: { params: { slug: string } }) {
208
+ const { metadata, content } = getPostBySlug<PostMeta>(params.slug);
209
+
210
+ return (
211
+ <article>
212
+ <h1>{metadata.title}</h1>
213
+ <p>{metadata.date}</p>
214
+ <div dangerouslySetInnerHTML={{ __html: content }} />
215
+ </article>
216
+ );
217
+ }
218
+ ```
219
+
220
+ ---
221
+
222
+ ### Pages Router
223
+
224
+ ```ts
225
+ // pages/blog/[slug].tsx
226
+ import { GetStaticPaths, GetStaticProps } from "next";
227
+ import { getAllPostParams, getPostBySlug } from "next-posts";
228
+
229
+ interface PostMeta {
230
+ title: string;
231
+ date: string;
232
+ }
233
+
234
+ export const getStaticPaths: GetStaticPaths = () => ({
235
+ paths: getAllPostParams().map(({ slug }) => ({ params: { slug } })),
236
+ fallback: false,
237
+ });
238
+
239
+ export const getStaticProps: GetStaticProps = ({ params }) => {
240
+ const post = getPostBySlug<PostMeta>(params!.slug as string);
241
+ return { props: { post } };
242
+ };
243
+ ```
244
+
245
+ ---
246
+
247
+ ## 自訂文章目錄
248
+
249
+ 所有函式皆支援傳入自訂目錄:
250
+
251
+ ```ts
252
+ const posts = getAllPosts("content/articles/");
253
+ const post = getPostBySlug("my-article", "content/articles/");
254
+ ```
255
+
256
+ ---
257
+
258
+ ## 從 v0.x 版本遷移
259
+
260
+ 遷移過程很容易,因為大部分的 API 都保持兼容。
261
+
262
+ 1. 變更套件名稱:
263
+
264
+ `next-posts` 過去稱為 `next-staticblog` ,你可以直接替換套件名稱並指向最新版本。舊的套件將棄用並不再進行維護。
265
+
266
+ 2. 明確的型別:
267
+
268
+ 在 `v0.x` 版本中使用 `any` 來定義 `metadata`,在 `v1.x` 版本中改為使用 `unknown` 來提升安全性。
269
+ 請閱讀文檔瞭解如何傳入泛型,擁有安全的型別。
270
+
271
+ 除此之外套件移除了 `gray-matter` 並改用 `@std/yaml` 進行解析。雖然變更通過了所有的測試,但仍然可能在部分非標準用法上有細微差異。
272
+
273
+ ---
274
+
275
+ ## 貢獻
276
+
277
+ 你可以透過回報問題或提交新功能完善這個套件!
278
+
279
+ ```bash
280
+ # 安裝依賴
281
+ npm install
282
+
283
+ # 建置
284
+ npm run build
285
+
286
+ # 執行測試
287
+ npm test
288
+
289
+ # 格式化程式碼
290
+ npm run format
291
+ ```
292
+
293
+ ---
294
+
295
+ ## 授權
296
+
297
+ MIT © yd-tw
298
+ [https://github.com/yd-tw/next-posts](https://github.com/yd-tw/next-posts)
package/README.md CHANGED
@@ -1,3 +1,260 @@
1
- # next-staticblog
2
-
3
- Quickly configure the markdown component in the Next.js project and create a blog page!
1
+ # next-staticblog
2
+
3
+ > Quickly configure the markdown component in a Next.js project and create a blog page!
4
+
5
+ A lightweight, zero-runtime-dependency TypeScript library for building static blogs in Next.js. It parses markdown files with YAML frontmatter and provides simple utilities to retrieve posts for use with static site generation (SSG).
6
+
7
+ ## Features
8
+
9
+ - Zero runtime dependencies — uses Node.js built-ins only
10
+ - Full TypeScript support with generic metadata types
11
+ - Works with any directory structure
12
+ - Handles both `slug` and `slug.md` inputs transparently
13
+ - Compatible with Next.js App Router and Pages Router
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install next-staticblog
19
+ ```
20
+
21
+ ```bash
22
+ pnpm add next-staticblog
23
+ ```
24
+
25
+ ```bash
26
+ yarn add next-staticblog
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ Place your markdown files in a `posts/` directory at the project root:
32
+
33
+ ```
34
+ my-next-app/
35
+ └── posts/
36
+ ├── hello-world.md
37
+ └── getting-started.md
38
+ ```
39
+
40
+ Each file should contain a YAML frontmatter block followed by the post content:
41
+
42
+ ```markdown
43
+ ---
44
+ title: Hello World
45
+ date: 2024-01-01
46
+ tags:
47
+ - blog
48
+ - intro
49
+ ---
50
+
51
+ Welcome to my blog!
52
+ ```
53
+
54
+ Then use the library in your Next.js pages:
55
+
56
+ ```typescript
57
+ import { getAllPosts, getPostBySlug, getAllPostParams } from "next-staticblog";
58
+
59
+ interface PostMeta {
60
+ title: string;
61
+ date: string;
62
+ tags?: string[];
63
+ }
64
+
65
+ // Get all posts
66
+ const posts = getAllPosts<PostMeta>();
67
+
68
+ // Get a single post
69
+ const post = getPostBySlug<PostMeta>("hello-world");
70
+
71
+ // Generate static paths (Next.js)
72
+ const paths = getAllPostParams();
73
+ ```
74
+
75
+ ## API
76
+
77
+ ### `getAllPosts<T>(directory?)`
78
+
79
+ Returns all posts from the specified directory, each with parsed frontmatter and content.
80
+
81
+ ```typescript
82
+ const posts = getAllPosts<PostMeta>();
83
+ // [{ slug: 'hello-world', metadata: { title: '...' }, content: '...' }, ...]
84
+ ```
85
+
86
+ | Parameter | Type | Default | Description |
87
+ | ----------- | -------- | ---------- | -------------------------------- |
88
+ | `directory` | `string` | `"posts/"` | Path relative to `process.cwd()` |
89
+
90
+ **Returns:** `Array<{ slug: string; metadata: T; content: string }>`
91
+
92
+ ---
93
+
94
+ ### `getPostBySlug<T>(slug, directory?)`
95
+
96
+ Returns a single post by slug. Accepts slugs with or without the `.md` extension.
97
+
98
+ ```typescript
99
+ const post = getPostBySlug<PostMeta>("hello-world");
100
+ // { slug: 'hello-world', metadata: { title: 'Hello World', ... }, content: '...' }
101
+ ```
102
+
103
+ | Parameter | Type | Default | Description |
104
+ | ----------- | -------- | ---------- | -------------------------------- |
105
+ | `slug` | `string` | — | Filename with or without `.md` |
106
+ | `directory` | `string` | `"posts/"` | Path relative to `process.cwd()` |
107
+
108
+ **Returns:** `{ slug: string; metadata: T; content: string }`
109
+
110
+ ---
111
+
112
+ ### `getAllPostSlugs(directory?)`
113
+
114
+ Returns the raw filenames (including `.md` extension) from the specified directory.
115
+
116
+ ```typescript
117
+ const slugs = getAllPostSlugs();
118
+ // ['hello-world.md', 'getting-started.md']
119
+ ```
120
+
121
+ | Parameter | Type | Default | Description |
122
+ | ----------- | -------- | ---------- | -------------------------------- |
123
+ | `directory` | `string` | `"posts/"` | Path relative to `process.cwd()` |
124
+
125
+ **Returns:** `string[]`
126
+
127
+ ---
128
+
129
+ ### `getAllPostParams(directory?)`
130
+
131
+ Returns slug parameter objects suitable for use with Next.js `generateStaticParams` or `getStaticPaths`. The `.md` extension is stripped automatically.
132
+
133
+ ```typescript
134
+ const params = getAllPostParams();
135
+ // [{ slug: 'hello-world' }, { slug: 'getting-started' }]
136
+ ```
137
+
138
+ | Parameter | Type | Default | Description |
139
+ | ----------- | -------- | ---------- | -------------------------------- |
140
+ | `directory` | `string` | `"posts/"` | Path relative to `process.cwd()` |
141
+
142
+ **Returns:** `Array<{ slug: string }>`
143
+
144
+ ---
145
+
146
+ ### `parseFrontmatter(input)`
147
+
148
+ Parses a raw markdown string and extracts YAML frontmatter metadata. Useful when you have markdown content not loaded from the filesystem, or when you need lower-level parsing control.
149
+
150
+ ```typescript
151
+ import { parseFrontmatter } from "next-staticblog";
152
+
153
+ const raw = `---
154
+ title: Hello World
155
+ date: 2024-01-01
156
+ tags:
157
+ - blog
158
+ ---
159
+
160
+ Welcome to my blog!`;
161
+
162
+ const { data, content } = parseFrontmatter(raw);
163
+ // data: { title: 'Hello World', date: '2024-01-01', tags: ['blog'] }
164
+ // content: '\nWelcome to my blog!'
165
+ ```
166
+
167
+ | Parameter | Type | Description |
168
+ | --------- | -------- | ------------------------------------ |
169
+ | `input` | `string` | Raw markdown string with frontmatter |
170
+
171
+ **Returns:** `{ data: Record<string, unknown>; content: string }`
172
+
173
+ - `data` — parsed YAML frontmatter as a plain object. Returns `{}` if no valid frontmatter is found.
174
+ - `content` — the markdown body after the frontmatter block.
175
+
176
+ ---
177
+
178
+ ## Usage with Next.js
179
+
180
+ ### App Router (Next.js 13+)
181
+
182
+ ```typescript
183
+ // app/blog/[slug]/page.tsx
184
+ import { getAllPostParams, getPostBySlug } from 'next-staticblog';
185
+
186
+ interface PostMeta {
187
+ title: string;
188
+ date: string;
189
+ }
190
+
191
+ export function generateStaticParams() {
192
+ return getAllPostParams();
193
+ }
194
+
195
+ export default function BlogPost({ params }: { params: { slug: string } }) {
196
+ const { metadata, content } = getPostBySlug<PostMeta>(params.slug);
197
+
198
+ return (
199
+ <article>
200
+ <h1>{metadata.title}</h1>
201
+ <p>{metadata.date}</p>
202
+ {/* render content with your preferred markdown renderer */}
203
+ <div dangerouslySetInnerHTML={{ __html: content }} />
204
+ </article>
205
+ );
206
+ }
207
+ ```
208
+
209
+ ### Pages Router
210
+
211
+ ```typescript
212
+ // pages/blog/[slug].tsx
213
+ import { GetStaticPaths, GetStaticProps } from "next";
214
+ import { getAllPostParams, getPostBySlug } from "next-staticblog";
215
+
216
+ interface PostMeta {
217
+ title: string;
218
+ date: string;
219
+ }
220
+
221
+ export const getStaticPaths: GetStaticPaths = () => ({
222
+ paths: getAllPostParams().map(({ slug }) => ({ params: { slug } })),
223
+ fallback: false,
224
+ });
225
+
226
+ export const getStaticProps: GetStaticProps = ({ params }) => {
227
+ const post = getPostBySlug<PostMeta>(params!.slug as string);
228
+ return { props: { post } };
229
+ };
230
+ ```
231
+
232
+ ### Custom Post Directory
233
+
234
+ All functions accept an optional `directory` parameter:
235
+
236
+ ```typescript
237
+ // Use a custom directory
238
+ const posts = getAllPosts("content/articles/");
239
+ const post = getPostBySlug("my-article", "content/articles/");
240
+ ```
241
+
242
+ ## Development
243
+
244
+ ```bash
245
+ # Install dependencies
246
+ npm install
247
+
248
+ # Build
249
+ npm run build
250
+
251
+ # Run tests
252
+ npm test
253
+
254
+ # Format code
255
+ npm run format
256
+ ```
257
+
258
+ ## License
259
+
260
+ MIT © [twyd](https://github.com/yd-tw/next-staticblog)
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  export declare function getAllPostSlugs(directory?: string): string[];
2
- export declare function getAllPosts(directory?: string): {
2
+ export declare function getAllPosts<T extends Record<string, unknown> = Record<string, unknown>>(directory?: string): {
3
3
  slug: string;
4
- metadata: Record<string, unknown>;
4
+ metadata: T;
5
5
  content: string;
6
6
  }[];
7
7
  export declare function getAllPostParams(directory?: string): {
8
8
  slug: string;
9
9
  }[];
10
- export declare function getPostBySlug(slug: string, directory?: string): {
10
+ export declare function getPostBySlug<T extends Record<string, unknown> = Record<string, unknown>>(slug: string, directory?: string): {
11
11
  slug: string;
12
- metadata: Record<string, unknown>;
12
+ metadata: T;
13
13
  content: string;
14
14
  };