@tenjuu99/blog 0.2.57 → 0.3.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 +1 -0
- package/docs/develop.md +89 -0
- package/docs/spec.md +101 -1
- package/lib/cssGenerator.js +4 -1
- package/lib/filter.js +12 -7
- package/lib/includeFilter.js +4 -3
- package/lib/pageData.js +7 -4
- package/lib/replaceVariablesFilter.js +4 -1
- package/package.json +1 -1
- package/packages/category/helper/categoryIndexer.js +42 -11
- package/src-sample/pages/article/news-200910.md +13 -0
- package/src-sample/pages/article/tech-update.md +14 -0
- package/src-sample/pages/book/after-rain.md +13 -0
- package/src-sample/pages/book/japanese-history.md +13 -0
- package/src-sample/pages/book/modern-sculpture.md +13 -0
package/README.md
CHANGED
package/docs/develop.md
CHANGED
|
@@ -238,6 +238,95 @@ packages/{パッケージ名}/
|
|
|
238
238
|
- `packages/editor/` - エディター機能
|
|
239
239
|
- `packages/turbolink/` - Turbolink機能
|
|
240
240
|
|
|
241
|
+
## パッケージのカスタマイズ方法
|
|
242
|
+
|
|
243
|
+
### 基本原則
|
|
244
|
+
|
|
245
|
+
パッケージが提供する任意のファイルを `src/` 配下に同じ構造で配置すると上書きできます。これは `.cache/` ディレクトリへの展開時に、`src/` の内容が `packages/` の内容を上書きするためです(`lib/dir.js:53-57`)。
|
|
246
|
+
|
|
247
|
+
### テンプレートのカスタマイズ
|
|
248
|
+
|
|
249
|
+
**例**: category パッケージのテンプレートを変更
|
|
250
|
+
|
|
251
|
+
1. `src/template/category.html` を作成
|
|
252
|
+
2. 独自のデザインを実装
|
|
253
|
+
3. `.cache/` を削除して再ビルド
|
|
254
|
+
|
|
255
|
+
```html
|
|
256
|
+
<!-- src/template/category.html -->
|
|
257
|
+
{include('meta.html')}
|
|
258
|
+
<h1>My Custom Category Page</h1>
|
|
259
|
+
{script}
|
|
260
|
+
const items = allData.filter(data => data.category === page.category)
|
|
261
|
+
include('item-list.html', { items })
|
|
262
|
+
{/script}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
パッケージのデフォルトテンプレート(`packages/category/template/category.html`)が `src/template/category.html` で上書きされます。
|
|
266
|
+
|
|
267
|
+
### CSS のカスタマイズ
|
|
268
|
+
|
|
269
|
+
**例**: breadcrumbs.css のスタイルを変更
|
|
270
|
+
|
|
271
|
+
1. `src/css/breadcrumbs.css` を作成
|
|
272
|
+
2. カスタムスタイルを記述
|
|
273
|
+
3. テンプレートからの読み込み方法は変更不要
|
|
274
|
+
|
|
275
|
+
```css
|
|
276
|
+
/* src/css/breadcrumbs.css */
|
|
277
|
+
.breadcrumbs {
|
|
278
|
+
/* カスタムスタイル */
|
|
279
|
+
background-color: #f0f0f0;
|
|
280
|
+
padding: 1rem;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
CSS は `.cache/css/` に展開され、通常の静的ファイルとして配信されます。テンプレート内で以下のように読み込みます:
|
|
285
|
+
|
|
286
|
+
```html
|
|
287
|
+
<!-- src/template/css.html -->
|
|
288
|
+
<link rel="stylesheet" href="${/css/lazy.css<<page.css,markdown.css,breadcrumbs.css}">
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Helper のカスタマイズ
|
|
292
|
+
|
|
293
|
+
**例**: breadcrumbs ヘルパーの動作を変更
|
|
294
|
+
|
|
295
|
+
1. `src/helper/breadcrumbs.js` を作成
|
|
296
|
+
2. 独自の実装を提供
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
// src/helper/breadcrumbs.js
|
|
300
|
+
export function breadcrumbs(page) {
|
|
301
|
+
// カスタムロジック
|
|
302
|
+
return page.url.split('/').filter(Boolean).map((segment, i, arr) => {
|
|
303
|
+
const path = '/' + arr.slice(0, i + 1).join('/')
|
|
304
|
+
return `<a href="${path}">${segment}</a>`
|
|
305
|
+
}).join(' > ')
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
パッケージのデフォルト実装(`packages/breadcrumbs/helper/breadcrumbs.js`)が上書きされます。
|
|
310
|
+
|
|
311
|
+
### 部分的な拡張
|
|
312
|
+
|
|
313
|
+
パッケージの一部だけを利用し、他をカスタマイズすることも可能です:
|
|
314
|
+
|
|
315
|
+
- `packages/category/template/category.html` → `src/template/category.html` で上書き
|
|
316
|
+
- `packages/category/helper/category.js` → そのまま使用(上書きしない)
|
|
317
|
+
- CSS は追加ファイルで拡張(`src/css/category-custom.css` など)
|
|
318
|
+
|
|
319
|
+
### .cache/ の再生成
|
|
320
|
+
|
|
321
|
+
キャッシュの問題が発生した場合や、パッケージのカスタマイズが反映されない場合:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
rm -rf .cache/
|
|
325
|
+
npm run dev # または npm run generate
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`.cache/` を削除すると、次回のビルド時に `lib/dir.js` の `cache()` 関数が再度実行され、パッケージとユーザーコードが正しい優先順位で展開されます。
|
|
329
|
+
|
|
241
330
|
## デバッグ方法
|
|
242
331
|
|
|
243
332
|
### 1. console.log デバッグ
|
package/docs/spec.md
CHANGED
|
@@ -421,6 +421,8 @@ helper.readIndex('/post')
|
|
|
421
421
|
|
|
422
422
|
| オプション | 説明 | デフォルト |
|
|
423
423
|
|-----------|------|-----------|
|
|
424
|
+
| `name` | カテゴリーシステムの識別子(`categories` 配列使用時のみ) | - |
|
|
425
|
+
| `path_filter` | 対象とするページのパスプレフィックス(`categories` 配列使用時に推奨) | `""` (全ページ) |
|
|
424
426
|
| `template` | カテゴリーページのテンプレート | `category.html` |
|
|
425
427
|
| `auto_generate` | 自動生成の有効/無効 | `true` |
|
|
426
428
|
| `max_depth` | カテゴリーの最大階層数 | `3` |
|
|
@@ -430,6 +432,13 @@ helper.readIndex('/post')
|
|
|
430
432
|
|
|
431
433
|
**設定オプションの詳細:**
|
|
432
434
|
|
|
435
|
+
- **`name`**: カテゴリーシステムの識別子。`categories` 配列で複数のカテゴリーシステムを定義する際に使用します。
|
|
436
|
+
|
|
437
|
+
- **`path_filter`**: 対象とするページのパスプレフィックス。
|
|
438
|
+
- 例: `path_filter: "book/"` → `book/` 配下のページのみがこのカテゴリーシステムに含まれる
|
|
439
|
+
- 空文字列の場合は全ページが対象
|
|
440
|
+
- 複数カテゴリーシステムを使用する場合は、各システムで `path_filter` を設定することを推奨
|
|
441
|
+
|
|
433
442
|
- **`url_separator`**: カテゴリー名にスペースが含まれる場合の置き換え文字を指定します。
|
|
434
443
|
- 例: `category: ["Contemporary Art"]` + `url_separator: "-"` → `/contemporary-art/`
|
|
435
444
|
- 例: `category: ["Contemporary Art"]` + `url_separator: "_"` → `/contemporary_art/`
|
|
@@ -462,6 +471,58 @@ helper.readIndex('/post')
|
|
|
462
471
|
- `{{category_pages}}` - このカテゴリーに属するページ名の配列
|
|
463
472
|
- `{{category_children}}` - サブカテゴリーのURL配列
|
|
464
473
|
|
|
474
|
+
**複数カテゴリーシステムの使用:**
|
|
475
|
+
|
|
476
|
+
`categories` 配列を使用することで、複数の独立したカテゴリーシステムを定義できます。
|
|
477
|
+
|
|
478
|
+
```json
|
|
479
|
+
{
|
|
480
|
+
"packages": "category",
|
|
481
|
+
"hooks": {
|
|
482
|
+
"afterIndexing": "categoryIndexer.js"
|
|
483
|
+
},
|
|
484
|
+
"categories": [
|
|
485
|
+
{
|
|
486
|
+
"name": "books",
|
|
487
|
+
"url_prefix": "/book-list",
|
|
488
|
+
"path_filter": "book/",
|
|
489
|
+
"template": "category.html",
|
|
490
|
+
"auto_generate": true,
|
|
491
|
+
"max_depth": 3,
|
|
492
|
+
"url_case": "lower",
|
|
493
|
+
"url_separator": "-"
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
"name": "articles",
|
|
497
|
+
"url_prefix": "/article-list",
|
|
498
|
+
"path_filter": "article/",
|
|
499
|
+
"template": "article-category.html",
|
|
500
|
+
"auto_generate": true,
|
|
501
|
+
"max_depth": 2
|
|
502
|
+
}
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**動作例:**
|
|
508
|
+
- `book/after-rain.md` (category: ["Art", "Painting"]) → `/book-list/art/painting/index.html`
|
|
509
|
+
- `article/news/200910.md` (category: ["News"]) → `/article-list/news/index.html`
|
|
510
|
+
- `/book-list/news/index.html` は生成**されない**(意図しないページ生成を防止)
|
|
511
|
+
- `/article-list/art/index.html` は生成**されない**(各システムは独立)
|
|
512
|
+
|
|
513
|
+
**後方互換性:**
|
|
514
|
+
|
|
515
|
+
既存の `category`(単数形)設定も引き続き動作します。`categories` 配列と `category` の両方が定義されている場合、`categories` が優先されます。
|
|
516
|
+
|
|
517
|
+
```json
|
|
518
|
+
{
|
|
519
|
+
"category": {
|
|
520
|
+
"template": "category.html",
|
|
521
|
+
"auto_generate": true
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
465
526
|
**手動ページによる上書き:**
|
|
466
527
|
|
|
467
528
|
`src/pages/tech/index.md` が存在する場合、自動生成はスキップされます。
|
|
@@ -643,7 +704,46 @@ export async function afterIndexing(allData, config) {
|
|
|
643
704
|
|
|
644
705
|
## ファイル操作とキャッシュ
|
|
645
706
|
|
|
646
|
-
### `.cache/`
|
|
707
|
+
### `.cache/` ディレクトリの役割
|
|
708
|
+
|
|
709
|
+
#### 概要
|
|
710
|
+
|
|
711
|
+
`.cache/` はパッケージとユーザーコードを統合した「実効的なソースディレクトリ」として機能します。ビルドシステムはこのディレクトリを実際のソースとして扱います。
|
|
712
|
+
|
|
713
|
+
#### ファイル展開の仕組み
|
|
714
|
+
|
|
715
|
+
`lib/dir.js` の `cache()` 関数が以下の順序で実行します:
|
|
716
|
+
|
|
717
|
+
1. **コアパッケージの展開**: `packages/*/` → `.cache/`(名前空間フラット化)
|
|
718
|
+
2. **ユーザーパッケージの展開**: `src/packages/*/` → `.cache/`
|
|
719
|
+
3. **ユーザーコードのコピー**: `src/` → `.cache/`(上書き)
|
|
720
|
+
|
|
721
|
+
**展開例**:
|
|
722
|
+
```
|
|
723
|
+
packages/breadcrumbs/helper/breadcrumbs.js → .cache/helper/breadcrumbs.js
|
|
724
|
+
packages/breadcrumbs/css/breadcrumbs.css → .cache/css/breadcrumbs.css
|
|
725
|
+
packages/category/template/category.html → .cache/template/category.html
|
|
726
|
+
src/helper/custom.js → .cache/helper/custom.js(追加)
|
|
727
|
+
src/template/category.html → .cache/template/category.html(上書き)
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
#### ファイル優先順位
|
|
731
|
+
|
|
732
|
+
同名ファイルが存在する場合の優先順位:
|
|
733
|
+
|
|
734
|
+
1. **`src/`** (最優先 - ユーザーカスタム)
|
|
735
|
+
2. **`src/packages/`** (ユーザー定義パッケージ)
|
|
736
|
+
3. **`packages/`** (コアパッケージ)
|
|
737
|
+
|
|
738
|
+
この仕組みにより、ユーザーはパッケージが提供する任意のファイルを上書きしてカスタマイズできます。
|
|
739
|
+
|
|
740
|
+
#### Helper の自動登録
|
|
741
|
+
|
|
742
|
+
`blog.json` の `packages` で指定されたパッケージは:
|
|
743
|
+
- `.cache/helper/<package>.js` が自動的に `config.helper` に追加される(`lib/dir.js:38-40, 45-47`)
|
|
744
|
+
- ユーザーが明示的に設定する必要はない
|
|
745
|
+
|
|
746
|
+
#### ディレクトリ構造
|
|
647
747
|
|
|
648
748
|
ビルド時に以下がコピーされます:
|
|
649
749
|
|
package/lib/cssGenerator.js
CHANGED
|
@@ -11,6 +11,9 @@ import config from './config.js'
|
|
|
11
11
|
let cacheBuster = {}
|
|
12
12
|
const cacheBusterQuery = 't'
|
|
13
13
|
|
|
14
|
+
// 正規表現をモジュールレベルでキャッシュ
|
|
15
|
+
const CSS_LINK_REGEXP = /\${([\w-_/]+\.css)<<([\w-_/,.]+\.css)}/g
|
|
16
|
+
|
|
14
17
|
/**
|
|
15
18
|
* @param {string} src
|
|
16
19
|
* @param {string} dist
|
|
@@ -51,7 +54,7 @@ const cssGenerator = async (src, dist) => {
|
|
|
51
54
|
* @return {string}
|
|
52
55
|
*/
|
|
53
56
|
const applyCss = async (text) => {
|
|
54
|
-
const target = [...text.matchAll(
|
|
57
|
+
const target = [...text.matchAll(CSS_LINK_REGEXP)].map(val => {
|
|
55
58
|
return { matched: val[0], dist: val[1], src: val[2] }
|
|
56
59
|
})
|
|
57
60
|
for (const cssDist of target) {
|
package/lib/filter.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import helper from '../lib/helper.js'
|
|
2
2
|
|
|
3
|
+
// 正規表現をモジュールレベルでキャッシュ
|
|
4
|
+
const IF_REGEXP = /(\{|\<)if\s(?<condition>[\s\S]+?)(}|>)(?<content>[\s\S]*?)((\{|\<)else(\}|\>)(?<elsecontent>[\s\S]*?))?(\{|\<)\/if(\>|})/g
|
|
5
|
+
const SCRIPT_REGEXP = /({script}|\<script\s.*type="ssg".*>)(?<script>[\s\S]*?)(\{\/script}|\<\/script>)/g
|
|
6
|
+
const CONDITION_SEGMENTS_REGEXP = /(?<left>[\S]+)\s(?<operator>!=|==)\s(?<right>[\S]+)/
|
|
7
|
+
const FUNCTION_CALL_REGEXP = /([\w]+)\((.*)\)/
|
|
8
|
+
const FUNCTION_ARG_REGEXP = /^(['"])?\s*([\w]+)(["'])?$/
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* @param {string} condition
|
|
5
12
|
* @params {object} variables
|
|
@@ -7,7 +14,7 @@ import helper from '../lib/helper.js'
|
|
|
7
14
|
*/
|
|
8
15
|
const ifConditionEvaluator = (condition, variables) => {
|
|
9
16
|
if (condition.includes('=')) {
|
|
10
|
-
const segmented = condition.match(
|
|
17
|
+
const segmented = condition.match(CONDITION_SEGMENTS_REGEXP)
|
|
11
18
|
let {left, operator, right} = segmented.groups
|
|
12
19
|
if (variables.hasOwnProperty(left)) {
|
|
13
20
|
left = variables[left]
|
|
@@ -34,14 +41,14 @@ const ifConditionEvaluator = (condition, variables) => {
|
|
|
34
41
|
return left != right
|
|
35
42
|
}
|
|
36
43
|
} else {
|
|
37
|
-
const match = condition.match(
|
|
44
|
+
const match = condition.match(FUNCTION_CALL_REGEXP)
|
|
38
45
|
if (match) {
|
|
39
46
|
const func = match[1]
|
|
40
47
|
if (helper[func] instanceof Function) {
|
|
41
48
|
let args = match[2].trim()
|
|
42
49
|
if (args) {
|
|
43
50
|
args = args.split(',').map(arg => {
|
|
44
|
-
const match = arg.match(
|
|
51
|
+
const match = arg.match(FUNCTION_ARG_REGEXP)
|
|
45
52
|
if (match) {
|
|
46
53
|
return match[1] ? `${match[2]}` : variables[match[2]]
|
|
47
54
|
}
|
|
@@ -64,8 +71,7 @@ const ifConditionEvaluator = (condition, variables) => {
|
|
|
64
71
|
* @returns {string}
|
|
65
72
|
*/
|
|
66
73
|
const replaceIfFilter = (text, variables) => {
|
|
67
|
-
const
|
|
68
|
-
const matched = [...text.matchAll(ifRegexp)]
|
|
74
|
+
const matched = [...text.matchAll(IF_REGEXP)]
|
|
69
75
|
for (const item of matched) {
|
|
70
76
|
const target = item[0]
|
|
71
77
|
const content = item.groups.content
|
|
@@ -83,8 +89,7 @@ const replaceIfFilter = (text, variables) => {
|
|
|
83
89
|
|
|
84
90
|
const replaceScriptFilter = async (text, variables) => {
|
|
85
91
|
let replaced = text
|
|
86
|
-
const
|
|
87
|
-
const scripts = [...text.matchAll(scriptRegexp)].map((matched) => {
|
|
92
|
+
const scripts = [...text.matchAll(SCRIPT_REGEXP)].map((matched) => {
|
|
88
93
|
return {
|
|
89
94
|
replace: matched[0],
|
|
90
95
|
script: matched.groups.script.trim("\n"),
|
package/lib/includeFilter.js
CHANGED
|
@@ -4,11 +4,12 @@ import { staticFile } from './files.js'
|
|
|
4
4
|
|
|
5
5
|
const alreadyLoaded = {}
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// 正規表現をモジュールレベルでキャッシュ
|
|
8
|
+
const INCLUDE_REGEXP = /\{\s*include\('(template|css)\/([\w\./-]+)'\)\s*\}/g
|
|
8
9
|
|
|
9
10
|
const includeFilter = (text) => {
|
|
10
11
|
let replaced = text
|
|
11
|
-
const include = [...text.matchAll(
|
|
12
|
+
const include = [...text.matchAll(INCLUDE_REGEXP)].map(matched => {
|
|
12
13
|
return { toBeReplace: matched[0], type: matched[1], filename: matched[2] }
|
|
13
14
|
})
|
|
14
15
|
if (include.length === 0) {
|
|
@@ -27,7 +28,7 @@ const includeFilter = (text) => {
|
|
|
27
28
|
throw new Error(cacheKey + ' is invalid')
|
|
28
29
|
}
|
|
29
30
|
// include を再帰的に解決する
|
|
30
|
-
if (content.match(
|
|
31
|
+
if (content.match(INCLUDE_REGEXP)) {
|
|
31
32
|
content = includeFilter(content)
|
|
32
33
|
}
|
|
33
34
|
alreadyLoaded[cacheKey] = content
|
package/lib/pageData.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import config from './config.js'
|
|
3
3
|
|
|
4
|
+
// 正規表現をモジュールレベルでキャッシュ
|
|
5
|
+
const FRONTMATTER_REGEXP = /^(<!|-)--(?<variables>[\s\S]*?)--(-|>)/
|
|
6
|
+
const METADATA_KEY_REGEXP = /^([a-zA-Z\d_-]+):/
|
|
7
|
+
|
|
4
8
|
const makePageData = (filename, content) => {
|
|
5
9
|
const [name, ext] = filename.split('.')
|
|
6
10
|
return parse(content, name, ext)
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
const parse = (content, name, ext) => {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const markdownReplaced = content.replace(regexp, '')
|
|
14
|
+
const matched = content.match(FRONTMATTER_REGEXP)
|
|
15
|
+
const markdownReplaced = content.replace(FRONTMATTER_REGEXP, '')
|
|
13
16
|
const fullUrl = (data) => {
|
|
14
17
|
const base = data.url_base + (data.relative_path ?? '')
|
|
15
18
|
let url = data.url
|
|
@@ -64,7 +67,7 @@ const parseMetaData = (data) => {
|
|
|
64
67
|
let key, value
|
|
65
68
|
for (const line of data.split('\n')) {
|
|
66
69
|
if (!isStringContinue) {
|
|
67
|
-
const match = line.match(
|
|
70
|
+
const match = line.match(METADATA_KEY_REGEXP)
|
|
68
71
|
if (match) {
|
|
69
72
|
key = match[1]
|
|
70
73
|
value = line.slice(line.indexOf(':') + 1).trim()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import helper from './helper.js'
|
|
2
2
|
|
|
3
|
+
// 正規表現をモジュールレベルでキャッシュ
|
|
4
|
+
const VARIABLE_REGEXP = /(\\)?{{[\s]*([^{}]+)[\s]*}}/g
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* テキストに含まれる変数を一括変換する
|
|
5
8
|
* ヘルパー処理もここで行われる
|
|
@@ -9,7 +12,7 @@ import helper from './helper.js'
|
|
|
9
12
|
* @return {text}
|
|
10
13
|
*/
|
|
11
14
|
const replaceVariablesFilter = (text, variables) => {
|
|
12
|
-
const matched = [...text.matchAll(
|
|
15
|
+
const matched = [...text.matchAll(VARIABLE_REGEXP)]
|
|
13
16
|
const replace = Object.fromEntries(matched.map(match => [match[0], {variableName: match[2].trim().toLowerCase(), backslash: !!match[1]}]))
|
|
14
17
|
let replaced = text
|
|
15
18
|
for (const elm in replace) {
|
package/package.json
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { buildCategoryTree } from './category.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @param {Object}
|
|
7
|
-
* @param {Object} config -
|
|
4
|
+
* 単一カテゴリー設定に対してカテゴリーページを生成する
|
|
5
|
+
* @param {Object} allData - 全ページデータ(仮想ページの追加先)
|
|
6
|
+
* @param {Object} categoryConfig - 単一カテゴリー設定オブジェクト
|
|
7
|
+
* @param {Object} config - グローバル設定オブジェクト
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
function generateCategoryPages(allData, categoryConfig, config) {
|
|
10
|
+
// path_filter でページをフィルタ
|
|
11
|
+
const pathFilter = categoryConfig.path_filter || ''
|
|
12
|
+
let filteredData = allData
|
|
13
|
+
|
|
14
|
+
if (pathFilter) {
|
|
15
|
+
filteredData = {}
|
|
16
|
+
for (const [name, page] of Object.entries(allData)) {
|
|
17
|
+
if (name.startsWith(pathFilter)) {
|
|
18
|
+
filteredData[name] = page
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
//
|
|
16
|
-
const
|
|
23
|
+
// カテゴリーツリーを構築(category キーにラップして buildCategoryTree に渡す)
|
|
24
|
+
const treeConfig = { category: categoryConfig }
|
|
25
|
+
const tree = buildCategoryTree(filteredData, treeConfig)
|
|
17
26
|
|
|
18
27
|
// 各カテゴリーに対して仮想ページを生成
|
|
19
28
|
for (const [url, categoryData] of Object.entries(tree)) {
|
|
@@ -30,7 +39,7 @@ export async function afterIndexing(allData, config) {
|
|
|
30
39
|
url: url,
|
|
31
40
|
__output: `${url}/index.html`,
|
|
32
41
|
title: categoryData.title,
|
|
33
|
-
template:
|
|
42
|
+
template: categoryConfig.template || 'category.html',
|
|
34
43
|
markdown: '',
|
|
35
44
|
category_path: categoryData.path,
|
|
36
45
|
category_pages: categoryData.pages,
|
|
@@ -53,3 +62,25 @@ export async function afterIndexing(allData, config) {
|
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* afterIndexing フック関数
|
|
68
|
+
* カテゴリーツリーを構築し、仮想カテゴリーインデックスページを生成する
|
|
69
|
+
* `config.categories`(配列)と `config.category`(単一、後方互換)の両方をサポート。
|
|
70
|
+
* @param {Object} allData - 全ページデータ
|
|
71
|
+
* @param {Object} config - 設定オブジェクト
|
|
72
|
+
*/
|
|
73
|
+
export async function afterIndexing(allData, config) {
|
|
74
|
+
// categories 配列または category 単一設定からカテゴリーシステム一覧を取得
|
|
75
|
+
const categorySystems = config.categories
|
|
76
|
+
? config.categories
|
|
77
|
+
: (config.category ? [config.category] : [])
|
|
78
|
+
|
|
79
|
+
for (const categoryConfig of categorySystems) {
|
|
80
|
+
if (categoryConfig.auto_generate === false) {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
generateCategoryPages(allData, categoryConfig, config)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tech Update Article
|
|
3
|
+
category: ["Tech", "News"]
|
|
4
|
+
published: 2024-01-20
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Tech Update Article
|
|
8
|
+
|
|
9
|
+
これは技術ニュース記事のページです。
|
|
10
|
+
|
|
11
|
+
カテゴリー: Tech > News
|
|
12
|
+
カテゴリー: [Tech]({{RELATIVE_PATH}}/article-list/tech/) > [News]({{RELATIVE_PATH}}/article-list/tech/news)
|
|
13
|
+
|
|
14
|
+
このページは `/article-list/tech/news/` カテゴリーに含まれるべきです。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Japanese History
|
|
3
|
+
category: ["History", "Japan"]
|
|
4
|
+
published: 2024-03-10
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Japanese History
|
|
8
|
+
|
|
9
|
+
これは書籍「Japanese History」のページです。
|
|
10
|
+
|
|
11
|
+
カテゴリー: [History]({{RELATIVE_PATH}}/book-list/history/) > [Japan]({{RELATIVE_PATH}}/book-list/history/japan/)
|
|
12
|
+
|
|
13
|
+
このページは `/book-list/history/japan/` カテゴリーに含まれるべきです。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Modern Sculpture
|
|
3
|
+
category: ["Art", "Sculpture"]
|
|
4
|
+
published: 2024-02-20
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Modern Sculpture
|
|
8
|
+
|
|
9
|
+
これは書籍「Modern Sculpture」のページです。
|
|
10
|
+
|
|
11
|
+
カテゴリー: [Art]({{RELATIVE_PATH}}/book-list/art/) > [Sculpture]({{RELATIVE_PATH}}/book-list/art/sculpture/)
|
|
12
|
+
|
|
13
|
+
このページは `/book-list/art/sculpture/` カテゴリーに含まれるべきです。
|