Skip to content

Commit 36316c3

Browse files
ericyangpanclaude
andcommitted
refactor(metadata): type-safe generators with dedicated parameter interfaces
- Add ListPageMetadataParams interface for list page generators - Add SoftwareDetailMetadataParams interface for software detail pages - Add ModelDetailMetadataParams interface for model detail pages - Add ComparisonMetadataParams interface for comparison pages - Add ArticleMetadataParams interface for article pages - Add DocsMetadataParams interface for documentation pages - Add StaticPageMetadataParams interface for static pages - Add metadata processing utilities in index.ts Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7d2fefb commit 36316c3

File tree

2 files changed

+163
-68
lines changed

2 files changed

+163
-68
lines changed

src/lib/metadata/generators.ts

Lines changed: 119 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,110 @@ import {
2828
} from './helpers'
2929
import { createPageMetadata, type PageType } from './templates'
3030

31+
// =============================================================================
32+
// SECTION: Parameter Type Interfaces
33+
// =============================================================================
34+
35+
/**
36+
* Parameters for generateListPageMetadata
37+
*/
38+
export interface ListPageMetadataParams {
39+
locale: Locale
40+
category: Category
41+
translationNamespace: string
42+
additionalKeywords?: string[]
43+
}
44+
45+
/**
46+
* Parameters for generateSoftwareDetailMetadata
47+
*/
48+
export interface SoftwareDetailMetadataParams {
49+
locale: Locale
50+
category: Category
51+
slug: string
52+
product: {
53+
name: string
54+
description: string
55+
vendor: string
56+
platforms?: Array<{ os: string }> | string[]
57+
pricing?: Array<{ value: number | null; currency?: string | null; per?: string | null }>
58+
license?: string
59+
}
60+
typeDescription: string
61+
}
62+
63+
/**
64+
* Parameters for generateModelDetailMetadata
65+
*/
66+
export interface ModelDetailMetadataParams {
67+
locale: Locale
68+
slug: string
69+
model: {
70+
name: string
71+
description: string
72+
vendor: string
73+
size?: string
74+
contextWindow?: number
75+
maxOutput?: number
76+
tokenPricing?: {
77+
input?: number
78+
output?: number
79+
}
80+
}
81+
translationNamespace: string
82+
}
83+
84+
/**
85+
* Parameters for generateComparisonMetadata
86+
*/
87+
export interface ComparisonMetadataParams {
88+
locale: Locale
89+
category: Category
90+
}
91+
92+
/**
93+
* Parameters for generateArticleMetadata
94+
*/
95+
export interface ArticleMetadataParams {
96+
locale: Locale
97+
slug: string
98+
article: {
99+
title: string
100+
description: string
101+
date: string
102+
author?: string
103+
}
104+
}
105+
106+
/**
107+
* Parameters for generateDocsMetadata
108+
*/
109+
export interface DocsMetadataParams {
110+
locale: Locale
111+
slug: string
112+
doc: {
113+
title: string
114+
description: string
115+
}
116+
}
117+
118+
/**
119+
* Parameters for generateStaticPageMetadata
120+
*/
121+
export interface StaticPageMetadataParams {
122+
locale: Locale
123+
basePath: string
124+
title: string
125+
description: string
126+
keywords?: string
127+
ogType?: 'website' | 'article'
128+
pageType?: 'home' | 'static' | 'search'
129+
}
130+
131+
// =============================================================================
132+
// SECTION: Generator Functions
133+
// =============================================================================
134+
31135
/**
32136
* Internal helper: Build complete metadata with alternates, OpenGraph, and Twitter Card
33137
* Consolidates the common pattern across all generators
@@ -100,12 +204,7 @@ function buildMetadataWithSocial(options: CommonMetadataOptions): Metadata {
100204
* Generate metadata for category list pages (IDEs, CLIs, etc.)
101205
* Returns complete metadata with robots rules via PageType 'list'
102206
*/
103-
export async function generateListPageMetadata(options: {
104-
locale: Locale
105-
category: Category
106-
translationNamespace: string
107-
additionalKeywords?: string[]
108-
}): Promise<Metadata> {
207+
export async function generateListPageMetadata(options: ListPageMetadataParams): Promise<Metadata> {
109208
const { locale, category, translationNamespace, additionalKeywords = [] } = options
110209

111210
const tPage = await getTranslations({ locale, namespace: translationNamespace })
@@ -156,20 +255,9 @@ export async function generateListPageMetadata(options: {
156255
* Generate metadata for software product detail pages (IDEs, CLIs, Extensions)
157256
* Returns complete metadata with robots rules via PageType 'detail'
158257
*/
159-
export async function generateSoftwareDetailMetadata(options: {
160-
locale: Locale
161-
category: Category
162-
slug: string
163-
product: {
164-
name: string
165-
description: string
166-
vendor: string
167-
platforms?: Array<{ os: string }> | string[]
168-
pricing?: Array<{ value: number | null; currency?: string | null; per?: string | null }>
169-
license?: string
170-
}
171-
typeDescription: string
172-
}): Promise<Metadata> {
258+
export async function generateSoftwareDetailMetadata(
259+
options: SoftwareDetailMetadataParams
260+
): Promise<Metadata> {
173261
const { locale, category, slug, product, typeDescription } = options
174262

175263
// Build title
@@ -223,23 +311,9 @@ export async function generateSoftwareDetailMetadata(options: {
223311
* Generate metadata for model detail pages
224312
* Returns complete metadata with robots rules via PageType 'detail'
225313
*/
226-
export async function generateModelDetailMetadata(options: {
227-
locale: Locale
228-
slug: string
229-
model: {
230-
name: string
231-
description: string
232-
vendor: string
233-
size?: string
234-
contextWindow?: number
235-
maxOutput?: number
236-
tokenPricing?: {
237-
input?: number
238-
output?: number
239-
}
240-
}
241-
translationNamespace: string
242-
}): Promise<Metadata> {
314+
export async function generateModelDetailMetadata(
315+
options: ModelDetailMetadataParams
316+
): Promise<Metadata> {
243317
const { locale, slug, model, translationNamespace } = options
244318

245319
const tPage = await getTranslations({ locale, namespace: translationNamespace })
@@ -298,10 +372,9 @@ export async function generateModelDetailMetadata(options: {
298372
* Generate metadata for comparison pages
299373
* Returns complete metadata with robots rules via PageType 'comparison'
300374
*/
301-
export async function generateComparisonMetadata(options: {
302-
locale: Locale
303-
category: Category
304-
}): Promise<Metadata> {
375+
export async function generateComparisonMetadata(
376+
options: ComparisonMetadataParams
377+
): Promise<Metadata> {
305378
const { locale, category } = options
306379

307380
const categoryName = CATEGORY_DISPLAY_NAMES[category as keyof typeof CATEGORY_DISPLAY_NAMES] || ''
@@ -346,16 +419,7 @@ export async function generateComparisonMetadata(options: {
346419
* Generate metadata for article pages
347420
* Returns complete metadata with robots rules via PageType 'article'
348421
*/
349-
export async function generateArticleMetadata(options: {
350-
locale: Locale
351-
slug: string
352-
article: {
353-
title: string
354-
description: string
355-
date: string
356-
author?: string
357-
}
358-
}): Promise<Metadata> {
422+
export async function generateArticleMetadata(options: ArticleMetadataParams): Promise<Metadata> {
359423
const { locale, slug, article } = options
360424

361425
// Build title
@@ -390,14 +454,7 @@ export async function generateArticleMetadata(options: {
390454
* Generate metadata for documentation pages
391455
* Returns complete metadata with robots rules via PageType 'docs'
392456
*/
393-
export async function generateDocsMetadata(options: {
394-
locale: Locale
395-
slug: string
396-
doc: {
397-
title: string
398-
description: string
399-
}
400-
}): Promise<Metadata> {
457+
export async function generateDocsMetadata(options: DocsMetadataParams): Promise<Metadata> {
401458
const { locale, slug, doc } = options
402459

403460
// Build title
@@ -432,15 +489,9 @@ export async function generateDocsMetadata(options: {
432489
* This is a generic generator for pages that don't fit other specialized categories.
433490
* Use this for marketing pages, info pages, etc. to avoid hand-rolling metadata.
434491
*/
435-
export async function generateStaticPageMetadata(options: {
436-
locale: Locale
437-
basePath: string
438-
title: string
439-
description: string
440-
keywords?: string
441-
ogType?: 'website' | 'article'
442-
pageType?: 'home' | 'static' | 'search'
443-
}): Promise<Metadata> {
492+
export async function generateStaticPageMetadata(
493+
options: StaticPageMetadataParams
494+
): Promise<Metadata> {
444495
const {
445496
locale,
446497
basePath,

src/lib/metadata/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ export {
2828
SEO_CONFIG,
2929
SITE_CONFIG,
3030
} from './config'
31+
// Export generator parameter types
32+
export type {
33+
ArticleMetadataParams,
34+
ComparisonMetadataParams,
35+
DocsMetadataParams,
36+
ListPageMetadataParams,
37+
ModelDetailMetadataParams,
38+
SoftwareDetailMetadataParams,
39+
StaticPageMetadataParams,
40+
} from './generators'
3141
// Export all generators
3242
export {
3343
generateArticleMetadata,
@@ -57,6 +67,40 @@ export {
5767
getAlternateOGLocale,
5868
mapLocaleToOG,
5969
} from './helpers'
70+
export type { PageTranslationRequirement, TranslationValidationError } from './i18n-validation'
71+
// Export i18n translation validation
72+
export {
73+
assertPageTranslationComplete,
74+
getTranslationStats,
75+
PAGE_TRANSLATION_REQUIREMENTS,
76+
REQUIRED_TRANSLATION_KEYS,
77+
validateAllPageTranslations,
78+
validatePageTranslations,
79+
} from './i18n-validation'
80+
export type { GeneratorType, PageMetadataConfig } from './registry'
81+
// Export page registry
82+
export {
83+
getPageConfig,
84+
getPagesByCategory,
85+
getPagesByGeneratorType,
86+
getRegistryStats,
87+
getTotalPageCount,
88+
isRegisteredPage,
89+
PAGE_REGISTRY,
90+
pageRequiresSlug,
91+
} from './registry'
92+
export type {
93+
RecommendedMetadataFields,
94+
RequiredMetadataFields,
95+
ValidationError,
96+
} from './required-fields'
97+
// Export metadata validation
98+
export {
99+
assertMetadataComplete,
100+
hasRequiredFields,
101+
logMetadataSummary,
102+
validateMetadataCompleteness,
103+
} from './required-fields'
60104
// Export robots configuration
61105
export {
62106
DEFAULT_ROBOTS,

0 commit comments

Comments
 (0)