@@ -4,26 +4,47 @@ import crypto from "crypto";
44import ClientComponent from "./client" ;
55import { AvailableLocales } from "@/i18n/request" ;
66
7+ // generateStaticParams は既存のまま
8+
79export async function generateStaticParams ( ) {
810 const indexes = getArticleIndexes ( ) ;
911 return indexes . map ( ( article ) => {
10- const [ article_year , month , aid ] = article . slug . split ( "/" ) ;
11- return { article_year, month, aid } ;
12- } ) ;
12+ // slugが "YYYY/MM/AID" の形式であることを前提とする
13+ const parts = article . slug . split ( "/" ) ;
14+ // partsの最後の3つの要素を取り出すことで、"lang/YYYY/MM/AID"のような形式にも対応できるようにする
15+ if ( parts . length >= 3 ) {
16+ const [ article_year , month , aid ] = parts . slice ( - 3 ) ;
17+ return { article_year, month, aid } ;
18+ }
19+ // 予期しない形式のslugはスキップするか、ログを出す
20+ console . warn ( `Skipping invalid slug format in generateStaticParams: ${ article . slug } ` ) ;
21+ return null ; // nullを返して後でfilter(Boolean)で除外する
22+ } ) . filter ( Boolean ) ; // nullを除外
1323}
1424
25+
26+ // ArticleServer コンポーネントは既存のまま、または generateMetadata で取得した
27+ // データを再利用するようにリファクタリング可能ですが、ここでは generateMetadata の追加のみに留めます。
28+ // Next.js のApp Routerは、generateMetadata と ページコンポーネントで同じ
29+ // データ取得関数(getArticleIndexes, toHTMLなど)を呼び出した場合、
30+ // 可能であれば自動的に deduplicate (重複排除) してくれるため、
31+ // データの二重取得による大きなパフォーマンス低下は起きにくい設計になっています。
32+
1533export default async function ArticleServer ( {
1634 params,
1735} : {
1836 params : Promise < { article_year : string ; month : string ; aid : string } > ;
1937} ) {
2038 const resolvedParams = await params ;
2139 const indexes = getArticleIndexes ( ) ;
22- const articles = await toHTML ( indexes ) ;
40+ const articles = await toHTML ( indexes ) ; // ここで再度データを取得
2341 const slug = `${ resolvedParams . article_year } /${ resolvedParams . month } /${ resolvedParams . aid } ` ;
2442
25- const headings_list = articles
26- . filter ( ( a ) => a . slug === slug )
43+ // スラッグに一致する記事のみをフィルタリング
44+ const articlesForSlug = articles . filter ( ( a ) => a . slug === slug ) ;
45+
46+ // TOC生成ロジック (既存のまま)
47+ const headings_list = articlesForSlug
2748 . map ( ( article ) => {
2849 return {
2950 lang : article . lang as AvailableLocales ,
@@ -35,21 +56,25 @@ export default async function ArticleServer({
3556 toc : headings . toc . map ( ( heading ) => {
3657 const text = heading . replace ( / < .* ?> / g, "" ) . trim ( ) ;
3758 const id = crypto . createHash ( "sha512" ) . update ( text ) . digest ( "hex" ) ;
38- const level = "index-" + heading . slice ( 1 , 3 ) ;
59+ // headingタグからレベルを抽出する処理をより頑健に
60+ const levelMatch = heading . match ( / ^ < h ( [ 1 - 6 ] ) > / ) ;
61+ const level = levelMatch ? `index-h${ levelMatch [ 1 ] } ` : 'index-heading' ; // 例: <h1> -> index-h1
3962 return { id, text, level } ;
4063 } ) ,
4164 lang : headings . lang ,
4265 } ;
4366 } ) ;
4467
45- const processedContents = articles
46- . filter ( ( a ) => a . slug === slug )
68+ // コンテンツ処理ロジック (既存のまま)
69+ const processedContents = articlesForSlug
4770 . map ( ( article ) => {
4871 return {
4972 content : article . content . replace (
5073 / < h ( [ 1 - 6 ] ) > ( .* ?) < \/ h [ 1 - 6 ] > / g,
5174 ( match , level , text ) => {
75+ // 見出しレベルを1つ下げる(h1 -> h2, h2 -> h3など)
5276 const newLevel = Math . min ( parseInt ( level ) + 1 , 6 ) ;
77+ // IDは元の見出しテキストから生成
5378 const id = crypto . createHash ( "sha512" ) . update ( text ) . digest ( "hex" ) ;
5479 return `<h${ newLevel } id="${ id } ">${ text } </h${ newLevel } >` ;
5580 }
@@ -60,10 +85,13 @@ export default async function ArticleServer({
6085
6186 return (
6287 < ClientComponent
63- articles = { articles }
88+ // ClientComponentに渡すarticlesは、スラッグでフィルタリングしたものを渡すのが効率的ですが、
89+ // 元のコードに合わせてarticles全体(またはClientComponentが必要とするデータ構造)を渡します。
90+ // ここではarticles全体を渡す元のロジックを踏襲します。
91+ articles = { articles } // 全記事データを渡す (元のコード通り)
6492 slug = { slug }
65- tocs = { tocs }
66- processedContents = { processedContents }
93+ tocs = { tocs } // フィルタリング済みのTOCs
94+ processedContents = { processedContents } // フィルタリング済みのコンテンツ
6795 />
6896 ) ;
69- }
97+ }
0 commit comments