Skip to content

Commit 9a1d574

Browse files
ericyangpanclaude
andcommitted
refactor(lib): consolidate metadata utilities and add data fetchers
Consolidate metadata utilities and add centralized data fetchers for better code organization. Changes: - Update metadata generators and helpers with improved type safety - Remove deprecated seo-helpers.ts - Add new data fetchers module (src/lib/data/fetchers.ts) for centralized data fetching with React cache() - Update license component with new i18n structure - Remove generated backup directories: - src/lib/generated.backup/** - src/lib/generated.formatted/** These changes improve maintainability and align with the new i18n architecture. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 51259b8 commit 9a1d574

File tree

5 files changed

+128
-112
lines changed

5 files changed

+128
-112
lines changed

src/lib/data/fetchers.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { cache } from 'react'
2+
import type { Locale } from '@/i18n/config'
3+
import {
4+
clisData,
5+
extensionsData,
6+
idesData,
7+
modelsData,
8+
providersData,
9+
vendorsData,
10+
} from '@/lib/generated'
11+
import { getArticleBySlug } from '@/lib/generated/articles'
12+
import { getDocBySlug } from '@/lib/generated/docs'
13+
import { localizeManifestItem } from '@/lib/manifest-i18n'
14+
import type {
15+
ManifestCLI,
16+
ManifestExtension,
17+
ManifestIDE,
18+
ManifestModel,
19+
ManifestProvider,
20+
ManifestVendor,
21+
} from '@/types/manifests'
22+
23+
/**
24+
* Cached fetcher for IDE data
25+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
26+
*/
27+
export const getIDE = cache(async (slug: string, locale: Locale) => {
28+
const ideRaw = idesData.find(i => i.id === slug)
29+
if (!ideRaw) return null
30+
31+
return localizeManifestItem(
32+
ideRaw as unknown as Record<string, unknown>,
33+
locale
34+
) as unknown as ManifestIDE
35+
})
36+
37+
/**
38+
* Cached fetcher for CLI data
39+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
40+
*/
41+
export const getCLI = cache(async (slug: string, locale: Locale) => {
42+
const cliRaw = clisData.find(c => c.id === slug)
43+
if (!cliRaw) return null
44+
45+
return localizeManifestItem(
46+
cliRaw as unknown as Record<string, unknown>,
47+
locale
48+
) as unknown as ManifestCLI
49+
})
50+
51+
/**
52+
* Cached fetcher for Model data
53+
* Models don't require localization, so no locale parameter needed
54+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
55+
*/
56+
export const getModel = cache(async (slug: string) => {
57+
return (modelsData.find(m => m.id === slug) as unknown as ManifestModel) || null
58+
})
59+
60+
/**
61+
* Cached fetcher for Extension data
62+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
63+
*/
64+
export const getExtension = cache(async (slug: string, locale: Locale) => {
65+
const extensionRaw = extensionsData.find(e => e.id === slug)
66+
if (!extensionRaw) return null
67+
68+
return localizeManifestItem(
69+
extensionRaw as unknown as Record<string, unknown>,
70+
locale
71+
) as unknown as ManifestExtension
72+
})
73+
74+
/**
75+
* Cached fetcher for Vendor data
76+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
77+
*/
78+
export const getVendor = cache(async (slug: string, locale: Locale) => {
79+
const vendorRaw = vendorsData.find(v => v.id === slug)
80+
if (!vendorRaw) return null
81+
82+
return localizeManifestItem(
83+
vendorRaw as unknown as Record<string, unknown>,
84+
locale
85+
) as unknown as ManifestVendor
86+
})
87+
88+
/**
89+
* Cached fetcher for Model Provider data
90+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
91+
*/
92+
export const getModelProvider = cache(async (slug: string, locale: Locale) => {
93+
const providerRaw = providersData.find(p => p.id === slug)
94+
if (!providerRaw) return null
95+
96+
return localizeManifestItem(
97+
providerRaw as unknown as Record<string, unknown>,
98+
locale
99+
) as unknown as ManifestProvider
100+
})
101+
102+
/**
103+
* Cached fetcher for Article data
104+
* Articles are already locale-aware through getArticleBySlug
105+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
106+
*/
107+
export const getArticle = cache(async (slug: string, locale: string) => {
108+
return getArticleBySlug(slug, locale)
109+
})
110+
111+
/**
112+
* Cached fetcher for Documentation data
113+
* Docs are already locale-aware through getDocBySlug
114+
* Uses React cache() to prevent duplicate fetching in generateMetadata and page component
115+
*/
116+
export const getDoc = cache(async (slug: string, locale: string) => {
117+
return getDocBySlug(slug, locale)
118+
})

src/lib/license.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { ReactElement } from 'react'
99
*/
1010
export function translateLicense(license: string, t: (key: string) => string): string {
1111
if (license.toLowerCase() === 'proprietary') {
12-
return t('license.proprietary')
12+
return t('shared.common.proprietary')
1313
}
1414
return license
1515
}

src/lib/metadata/generators.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
buildTwitterCard,
2525
formatPlatforms,
2626
formatPriceForDescription,
27-
getOGImagePath,
2827
} from './helpers'
2928

3029
/**
@@ -151,24 +150,23 @@ export async function generateSoftwareDetailMetadata(options: {
151150
languageBasePath: basePath,
152151
})
153152

154-
// Build OpenGraph with product image
153+
// Build OpenGraph
154+
// Note: OG images are automatically detected from opengraph-image.tsx files
155155
const canonicalPath = locale === defaultLocale ? `/${basePath}` : `/${locale}/${basePath}`
156-
const imagePath = getOGImagePath(category, slug)
157156

158157
const openGraph = buildOpenGraph({
159158
title: `${product.name} - ${typeDescription}`,
160159
description: product.description,
161160
url: canonicalPath,
162161
locale,
163162
type: 'article',
164-
images: [{ url: imagePath, alt: `${product.name} Screenshot` }],
165163
})
166164

167-
// Build Twitter Card with product image
165+
// Build Twitter Card
166+
// Note: Twitter images are automatically detected from opengraph-image.tsx files
168167
const twitter = buildTwitterCard({
169168
title: `${product.name} - ${typeDescription}`,
170169
description: product.description,
171-
images: [imagePath],
172170
})
173171

174172
return {
@@ -242,23 +240,22 @@ export async function generateModelDetailMetadata(options: {
242240
})
243241

244242
// Build OpenGraph
243+
// Note: OG images are automatically detected from opengraph-image.tsx files
245244
const canonicalPath = locale === defaultLocale ? `/${basePath}` : `/${locale}/${basePath}`
246-
const imagePath = getOGImagePath('models', slug)
247245

248246
const openGraph = buildOpenGraph({
249247
title: `${model.name} - ${t('metaTitle')}`,
250248
description: model.description,
251249
url: canonicalPath,
252250
locale,
253251
type: 'article',
254-
images: [{ url: imagePath, alt: `${model.name} Information` }],
255252
})
256253

257254
// Build Twitter Card
255+
// Note: Twitter images are automatically detected from opengraph-image.tsx files
258256
const twitter = buildTwitterCard({
259257
title: `${model.name} - ${t('metaTitle')}`,
260258
description: model.description,
261-
images: [imagePath],
262259
})
263260

264261
return {
@@ -384,24 +381,23 @@ export async function generateArticleMetadata(options: {
384381
})
385382

386383
// Build OpenGraph with article metadata
384+
// Note: OG images are automatically detected from opengraph-image.tsx files
387385
const canonicalPath = locale === defaultLocale ? `/${basePath}` : `/${locale}/${basePath}`
388-
const imagePath = getOGImagePath('articles', slug)
389386

390387
const openGraph = buildOpenGraph({
391388
title: article.title,
392389
description,
393390
url: canonicalPath,
394391
locale,
395392
type: 'article',
396-
images: [{ url: imagePath, alt: article.title }],
397393
publishedTime: article.date,
398394
})
399395

400396
// Build Twitter Card with creator
397+
// Note: Twitter images are automatically detected from opengraph-image.tsx files
401398
const twitter = buildTwitterCard({
402399
title: article.title,
403400
description,
404-
images: [imagePath],
405401
includeCreator: true,
406402
})
407403

src/lib/metadata/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* return await generateListPageMetadata({
1212
* locale: params.locale,
1313
* category: 'ides',
14-
* translationNamespace: 'stacksPages.ides',
14+
* translationNamespace: 'pages.ides',
1515
* });
1616
* }
1717
*/
@@ -53,6 +53,5 @@ export {
5353
formatPlatforms,
5454
formatPriceForDescription,
5555
getAlternateOGLocale,
56-
getOGImagePath,
5756
mapLocaleToOG,
5857
} from './helpers'

src/lib/seo-helpers.ts

Lines changed: 0 additions & 97 deletions
This file was deleted.

0 commit comments

Comments
 (0)