@x-wave/blog 2.9.1 → 2.10.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 +34 -2
- package/cli/index.js +48 -5
- package/index.js +5235 -5064
- package/locales/index.ts +6 -0
- package/package.json +1 -1
- package/styles/index.css +1 -1
- package/utils/markdown-alignment.d.ts +16 -0
- package/vite-config/setup-ssg.d.ts +2 -0
- package/vite-config/setup-ssg.d.ts.map +1 -1
- package/vite-config/setup-ssg.js +2 -1
- package/vite-config/setup-ssg.js.map +1 -1
- package/vite-config/static-gen-plugin.d.ts.map +1 -1
- package/vite-config/static-gen-plugin.js +112 -0
- package/vite-config/static-gen-plugin.js.map +1 -1
- package/vite-config/types.d.ts +2 -0
- package/vite-config/types.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -300,6 +300,29 @@ The blog system supports standard Markdown and extended syntax via GitHub Flavor
|
|
|
300
300
|
~~strikethrough~~
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
+
#### Text Alignment
|
|
304
|
+
|
|
305
|
+
Wrap content in `[left]`, `[center]`, or `[right]` tags. Inline Markdown remains
|
|
306
|
+
available inside aligned content:
|
|
307
|
+
|
|
308
|
+
```mdx
|
|
309
|
+
[center]This paragraph is **centered**.[/center]
|
|
310
|
+
|
|
311
|
+
[right]
|
|
312
|
+
This paragraph is right-aligned.
|
|
313
|
+
[/right]
|
|
314
|
+
|
|
315
|
+
[left]
|
|
316
|
+
|
|
317
|
+
- A left-aligned list item
|
|
318
|
+
- Another item
|
|
319
|
+
|
|
320
|
+
[/left]
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Use blank lines between the alignment tags and content when wrapping multiple
|
|
324
|
+
Markdown blocks such as headings, lists, or several paragraphs.
|
|
325
|
+
|
|
303
326
|
#### Lists
|
|
304
327
|
|
|
305
328
|
**Unordered lists:**
|
|
@@ -633,7 +656,8 @@ The home page displays:
|
|
|
633
656
|
- **Title**: "Latest Posts" (i18n translated)
|
|
634
657
|
- **Header metadata**: Author and date from the most recent article
|
|
635
658
|
- **Article cards**: Recent articles with title, description, author, and date
|
|
636
|
-
- **Sorting**: Articles sorted by date (newest first),
|
|
659
|
+
- **Sorting**: Articles sorted by date (newest first), with 20 articles per page
|
|
660
|
+
- **Pagination**: Crawlable archive URLs at `/{language}/page/{page}`
|
|
637
661
|
|
|
638
662
|
Article metadata comes from MDX frontmatter:
|
|
639
663
|
```mdx
|
|
@@ -673,13 +697,21 @@ Or add it as a project script:
|
|
|
673
697
|
|---|---|
|
|
674
698
|
| `--docs` | Path to the docs directory (must contain language sub-directories, e.g. `en/`, `es/`) |
|
|
675
699
|
| `--output` | Path to the output directory where JSON index files will be written |
|
|
700
|
+
| `--page-size` | Optional number of articles per page (default: `20`) |
|
|
701
|
+
|
|
702
|
+
If you customize `--page-size`, pass the same value as `articlesPerPage` to
|
|
703
|
+
`setupSSG()` so the JSON and statically generated archive pages stay aligned.
|
|
676
704
|
|
|
677
705
|
**Output structure** (one set per language):
|
|
678
706
|
|
|
679
707
|
```
|
|
680
708
|
public/blog-index/
|
|
681
709
|
├── en/
|
|
682
|
-
│ ├── latest.json # Latest
|
|
710
|
+
│ ├── latest.json # Latest 100 articles (legacy index)
|
|
711
|
+
│ ├── pages-index.json # Page count and page-size metadata
|
|
712
|
+
│ ├── pages/
|
|
713
|
+
│ │ ├── 1.json # First 20 articles
|
|
714
|
+
│ │ └── 2.json # Next 20 articles
|
|
683
715
|
│ ├── months-index.json # Sorted list of YYYY-MM strings
|
|
684
716
|
│ └── months/
|
|
685
717
|
│ └── 2026-02.json # Articles for that month
|
package/cli/index.js
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
* Options:
|
|
12
12
|
* --docs Path to the docs directory (must contain language sub-directories, e.g. en/, es/)
|
|
13
13
|
* --output Path to the output directory where JSON index files will be written
|
|
14
|
+
* --page-size Number of articles per home page (default: 20)
|
|
14
15
|
*
|
|
15
16
|
* Output structure (one set of files per language):
|
|
16
|
-
* <output>/<lang>/latest.json - Latest
|
|
17
|
+
* <output>/<lang>/latest.json - Latest 100 articles (legacy home page index)
|
|
18
|
+
* <output>/<lang>/pages-index.json - Pagination metadata for the home page
|
|
19
|
+
* <output>/<lang>/pages/<N>.json - Articles for a home page page
|
|
17
20
|
* <output>/<lang>/months-index.json - Sorted list of YYYY-MM strings with articles
|
|
18
21
|
* <output>/<lang>/months/<YYYY-MM>.json - Articles for a specific month
|
|
19
22
|
*
|
|
@@ -25,6 +28,7 @@ import fs from 'node:fs'
|
|
|
25
28
|
import path from 'node:path'
|
|
26
29
|
|
|
27
30
|
const LATEST_COUNT = 100
|
|
31
|
+
const DEFAULT_PAGE_SIZE = 20
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* Parse CLI arguments from process.argv.
|
|
@@ -36,6 +40,8 @@ function parseArgs(argv) {
|
|
|
36
40
|
args.docs = argv[++i]
|
|
37
41
|
} else if (argv[i] === '--output' && argv[i + 1]) {
|
|
38
42
|
args.output = argv[++i]
|
|
43
|
+
} else if (argv[i] === '--page-size' && argv[i + 1]) {
|
|
44
|
+
args.pageSize = Number(argv[++i])
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
return args
|
|
@@ -202,9 +208,13 @@ function processLanguage(docsDir, language) {
|
|
|
202
208
|
/**
|
|
203
209
|
* Write data as formatted JSON to a file, creating any missing directories.
|
|
204
210
|
*/
|
|
205
|
-
function writeJson(filePath, data) {
|
|
211
|
+
function writeJson(filePath, data, indentation = '\t') {
|
|
206
212
|
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
207
|
-
fs.writeFileSync(
|
|
213
|
+
fs.writeFileSync(
|
|
214
|
+
filePath,
|
|
215
|
+
`${JSON.stringify(data, null, indentation)}\n`,
|
|
216
|
+
'utf-8',
|
|
217
|
+
)
|
|
208
218
|
}
|
|
209
219
|
|
|
210
220
|
/**
|
|
@@ -214,12 +224,23 @@ async function main() {
|
|
|
214
224
|
const args = parseArgs(process.argv)
|
|
215
225
|
|
|
216
226
|
if (!args.docs || !args.output) {
|
|
217
|
-
console.error(
|
|
227
|
+
console.error(
|
|
228
|
+
'Usage: blog-indexer --docs <docs-dir> --output <output-dir> [--page-size <number>]',
|
|
229
|
+
)
|
|
230
|
+
process.exit(1)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
args.pageSize !== undefined &&
|
|
235
|
+
(!Number.isInteger(args.pageSize) || args.pageSize < 1)
|
|
236
|
+
) {
|
|
237
|
+
console.error('--page-size must be a positive integer')
|
|
218
238
|
process.exit(1)
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
const docsDir = path.resolve(args.docs)
|
|
222
242
|
const outputDir = path.resolve(args.output)
|
|
243
|
+
const pageSize = args.pageSize ?? DEFAULT_PAGE_SIZE
|
|
223
244
|
|
|
224
245
|
if (!fs.existsSync(docsDir)) {
|
|
225
246
|
console.error(`Docs directory not found: ${docsDir}`)
|
|
@@ -247,6 +268,28 @@ async function main() {
|
|
|
247
268
|
writeJson(path.join(langOutputDir, 'latest.json'), latest)
|
|
248
269
|
console.log(` ✓ latest.json (${latest.length} articles)`)
|
|
249
270
|
|
|
271
|
+
// Write paginated indexes for the home page. Keeping the pages separate means
|
|
272
|
+
// the browser only downloads the metadata it needs for the current page.
|
|
273
|
+
const totalPages = Math.ceil(sorted.length / pageSize)
|
|
274
|
+
writeJson(path.join(langOutputDir, 'pages-index.json'), {
|
|
275
|
+
pageSize,
|
|
276
|
+
totalArticles: sorted.length,
|
|
277
|
+
totalPages,
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
const pagesDir = path.join(langOutputDir, 'pages')
|
|
281
|
+
fs.rmSync(pagesDir, { recursive: true, force: true })
|
|
282
|
+
for (let page = 1; page <= totalPages; page++) {
|
|
283
|
+
const start = (page - 1) * pageSize
|
|
284
|
+
writeJson(
|
|
285
|
+
path.join(pagesDir, `${page}.json`),
|
|
286
|
+
sorted.slice(start, start + pageSize),
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
console.log(
|
|
290
|
+
` ✓ pages-index.json and ${totalPages} page file(s) (${pageSize} per page)`,
|
|
291
|
+
)
|
|
292
|
+
|
|
250
293
|
// Group articles by month (only articles with a parseable date)
|
|
251
294
|
/** @type {Map<string, typeof sorted>} */
|
|
252
295
|
const monthMap = new Map()
|
|
@@ -266,7 +309,7 @@ async function main() {
|
|
|
266
309
|
const monthsIndex = Array.from(monthMap.keys()).sort((a, b) =>
|
|
267
310
|
b.localeCompare(a),
|
|
268
311
|
)
|
|
269
|
-
writeJson(path.join(langOutputDir, 'months-index.json'), monthsIndex)
|
|
312
|
+
writeJson(path.join(langOutputDir, 'months-index.json'), monthsIndex, 0)
|
|
270
313
|
console.log(` ✓ months-index.json (${monthsIndex.length} months)`)
|
|
271
314
|
|
|
272
315
|
// Write one file per month
|