feat: Introduce metadata-only retrieval functions for posts, products, and pages to optimize sitemap generation.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 35s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m30s
Build & Deploy KLZ Cables / 🏗️ Build App (push) Successful in 4m39s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Successful in 46s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Has been cancelled
Build & Deploy KLZ Cables / ⚡ PageSpeed (push) Has been cancelled

This commit is contained in:
2026-02-06 17:01:25 +01:00
parent 57a3944301
commit 07e0f237b9
4 changed files with 151 additions and 31 deletions

View File

@@ -1,8 +1,10 @@
import { config } from '@/lib/config'; import { config } from '@/lib/config';
import { MetadataRoute } from 'next'; import { MetadataRoute } from 'next';
import { getAllProducts } from '@/lib/mdx'; import { getAllProductsMetadata } from '@/lib/mdx';
import { getAllPosts } from '@/lib/blog'; import { getAllPostsMetadata } from '@/lib/blog';
import { getAllPages } from '@/lib/pages'; import { getAllPagesMetadata } from '@/lib/pages';
export const revalidate = 3600; // Revalidate every hour
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = config.baseUrl || 'https://klz-cables.com'; const baseUrl = config.baseUrl || 'https://klz-cables.com';
@@ -34,11 +36,10 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
} }
// Products // Products
const products = await getAllProducts(locale); const productsMetadata = await getAllProductsMetadata(locale);
for (const product of products) { for (const product of productsMetadata) {
// We need to find the category for the product to build the URL if (!product.frontmatter || !product.slug) continue;
// In this project, products are under /products/[category]/[slug]
// The category is in product.frontmatter.categories
const category = const category =
product.frontmatter.categories[0]?.toLowerCase().replace(/\s+/g, '-') || 'other'; product.frontmatter.categories[0]?.toLowerCase().replace(/\s+/g, '-') || 'other';
sitemapEntries.push({ sitemapEntries.push({
@@ -50,8 +51,10 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
} }
// Blog posts // Blog posts
const posts = await getAllPosts(locale); const postsMetadata = await getAllPostsMetadata(locale);
for (const post of posts) { for (const post of postsMetadata) {
if (!post.frontmatter || !post.slug) continue;
sitemapEntries.push({ sitemapEntries.push({
url: `${baseUrl}/${locale}/blog/${post.slug}`, url: `${baseUrl}/${locale}/blog/${post.slug}`,
lastModified: new Date(post.frontmatter.date), lastModified: new Date(post.frontmatter.date),
@@ -61,8 +64,10 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
} }
// Static pages // Static pages
const pages = await getAllPages(locale); const pagesMetadata = await getAllPagesMetadata(locale);
for (const page of pages) { for (const page of pagesMetadata) {
if (!page.slug) continue;
sitemapEntries.push({ sitemapEntries.push({
url: `${baseUrl}/${locale}/${page.slug}`, url: `${baseUrl}/${locale}/${page.slug}`,
lastModified: new Date(), lastModified: new Date(),

View File

@@ -41,11 +41,11 @@ export async function getPostBySlug(slug: string, locale: string): Promise<PostM
export async function getAllPosts(locale: string): Promise<PostMdx[]> { export async function getAllPosts(locale: string): Promise<PostMdx[]> {
const postsDir = path.join(process.cwd(), 'data', 'blog', locale); const postsDir = path.join(process.cwd(), 'data', 'blog', locale);
if (!fs.existsSync(postsDir)) return []; if (!fs.existsSync(postsDir)) return [];
const files = fs.readdirSync(postsDir); const files = fs.readdirSync(postsDir);
const posts = files const posts = files
.filter(file => file.endsWith('.mdx')) .filter((file) => file.endsWith('.mdx'))
.map(file => { .map((file) => {
const filePath = path.join(postsDir, file); const filePath = path.join(postsDir, file);
const fileContent = fs.readFileSync(filePath, 'utf8'); const fileContent = fs.readFileSync(filePath, 'utf8');
const { data, content } = matter(fileContent); const { data, content } = matter(fileContent);
@@ -55,14 +55,42 @@ export async function getAllPosts(locale: string): Promise<PostMdx[]> {
content, content,
}; };
}) })
.sort((a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime()); .sort(
(a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime(),
);
return posts; return posts;
} }
export async function getAdjacentPosts(slug: string, locale: string): Promise<{ prev: PostMdx | null; next: PostMdx | null }> { export async function getAllPostsMetadata(locale: string): Promise<Partial<PostMdx>[]> {
const postsDir = path.join(process.cwd(), 'data', 'blog', locale);
if (!fs.existsSync(postsDir)) return [];
const files = fs.readdirSync(postsDir);
return files
.filter((file) => file.endsWith('.mdx'))
.map((file) => {
const filePath = path.join(postsDir, file);
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data } = matter(fileContent);
return {
slug: file.replace(/\.mdx$/, ''),
frontmatter: data as PostFrontmatter,
};
})
.sort(
(a, b) =>
new Date(b.frontmatter.date as string).getTime() -
new Date(a.frontmatter.date as string).getTime(),
);
}
export async function getAdjacentPosts(
slug: string,
locale: string,
): Promise<{ prev: PostMdx | null; next: PostMdx | null }> {
const posts = await getAllPosts(locale); const posts = await getAllPosts(locale);
const currentIndex = posts.findIndex(post => post.slug === slug); const currentIndex = posts.findIndex((post) => post.slug === slug);
if (currentIndex === -1) { if (currentIndex === -1) {
return { prev: null, next: null }; return { prev: null, next: null };

View File

@@ -18,11 +18,61 @@ export interface ProductMdx {
content: string; content: string;
} }
export async function getProductMetadata(
slug: string,
locale: string,
): Promise<Partial<ProductMdx> | null> {
// Map translated slug to file slug
const fileSlug = await mapSlugToFileSlug(slug, locale);
const productsDir = path.join(process.cwd(), 'data', 'products', locale);
// Try exact slug first
let filePath = path.join(productsDir, `${fileSlug}.mdx`);
if (!fs.existsSync(filePath)) {
// Try with -2 suffix (common in the dumped files)
filePath = path.join(productsDir, `${fileSlug}-2.mdx`);
}
if (!fs.existsSync(filePath)) {
// Fallback to English if locale is not 'en'
if (locale !== 'en') {
const enProductsDir = path.join(process.cwd(), 'data', 'products', 'en');
let enFilePath = path.join(enProductsDir, `${fileSlug}.mdx`);
if (!fs.existsSync(enFilePath)) {
enFilePath = path.join(enProductsDir, `${fileSlug}-2.mdx`);
}
if (fs.existsSync(enFilePath)) {
const fileContent = fs.readFileSync(enFilePath, 'utf8');
const { data } = matter(fileContent);
return {
slug: fileSlug,
frontmatter: {
...data,
isFallback: true,
} as ProductFrontmatter & { isFallback?: boolean },
};
}
}
} else {
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data } = matter(fileContent);
return {
slug: fileSlug,
frontmatter: data as ProductFrontmatter,
};
}
return null;
}
export async function getProductBySlug(slug: string, locale: string): Promise<ProductMdx | null> { export async function getProductBySlug(slug: string, locale: string): Promise<ProductMdx | null> {
// Map translated slug to file slug // Map translated slug to file slug
const fileSlug = await mapSlugToFileSlug(slug, locale); const fileSlug = await mapSlugToFileSlug(slug, locale);
const productsDir = path.join(process.cwd(), 'data', 'products', locale); const productsDir = path.join(process.cwd(), 'data', 'products', locale);
// Try exact slug first // Try exact slug first
let filePath = path.join(productsDir, `${fileSlug}.mdx`); let filePath = path.join(productsDir, `${fileSlug}.mdx`);
@@ -41,7 +91,7 @@ export async function getProductBySlug(slug: string, locale: string): Promise<Pr
if (!fs.existsSync(enFilePath)) { if (!fs.existsSync(enFilePath)) {
enFilePath = path.join(enProductsDir, `${fileSlug}-2.mdx`); enFilePath = path.join(enProductsDir, `${fileSlug}-2.mdx`);
} }
if (fs.existsSync(enFilePath)) { if (fs.existsSync(enFilePath)) {
const fileContent = fs.readFileSync(enFilePath, 'utf8'); const fileContent = fs.readFileSync(enFilePath, 'utf8');
const { data, content } = matter(fileContent); const { data, content } = matter(fileContent);
@@ -49,7 +99,7 @@ export async function getProductBySlug(slug: string, locale: string): Promise<Pr
slug: fileSlug, slug: fileSlug,
frontmatter: { frontmatter: {
...data, ...data,
isFallback: true isFallback: true,
} as ProductFrontmatter & { isFallback?: boolean }, } as ProductFrontmatter & { isFallback?: boolean },
content, content,
}; };
@@ -67,7 +117,12 @@ export async function getProductBySlug(slug: string, locale: string): Promise<Pr
} }
// Filter out products without images // Filter out products without images
if (product && (!product.frontmatter.images || product.frontmatter.images.length === 0 || !product.frontmatter.images[0])) { if (
product &&
(!product.frontmatter.images ||
product.frontmatter.images.length === 0 ||
!product.frontmatter.images[0])
) {
return null; return null;
} }
@@ -77,9 +132,9 @@ export async function getProductBySlug(slug: string, locale: string): Promise<Pr
export async function getAllProductSlugs(locale: string): Promise<string[]> { export async function getAllProductSlugs(locale: string): Promise<string[]> {
const productsDir = path.join(process.cwd(), 'data', 'products', locale); const productsDir = path.join(process.cwd(), 'data', 'products', locale);
if (!fs.existsSync(productsDir)) return []; if (!fs.existsSync(productsDir)) return [];
const files = fs.readdirSync(productsDir); const files = fs.readdirSync(productsDir);
return files.filter(file => file.endsWith('.mdx')).map(file => file.replace(/\.mdx$/, '')); return files.filter((file) => file.endsWith('.mdx')).map((file) => file.replace(/\.mdx$/, ''));
} }
export async function getAllProducts(locale: string): Promise<ProductMdx[]> { export async function getAllProducts(locale: string): Promise<ProductMdx[]> {
@@ -91,6 +146,19 @@ export async function getAllProducts(locale: string): Promise<ProductMdx[]> {
allSlugs = Array.from(new Set([...slugs, ...enSlugs])); allSlugs = Array.from(new Set([...slugs, ...enSlugs]));
} }
const products = await Promise.all(allSlugs.map(slug => getProductBySlug(slug, locale))); const products = await Promise.all(allSlugs.map((slug) => getProductBySlug(slug, locale)));
return products.filter((p): p is ProductMdx => p !== null); return products.filter((p): p is ProductMdx => p !== null);
} }
export async function getAllProductsMetadata(locale: string): Promise<Partial<ProductMdx>[]> {
const slugs = await getAllProductSlugs(locale);
let allSlugs = slugs;
if (locale !== 'en') {
const enSlugs = await getAllProductSlugs('en');
allSlugs = Array.from(new Set([...slugs, ...enSlugs]));
}
const metadata = await Promise.all(allSlugs.map((slug) => getProductMetadata(slug, locale)));
return metadata.filter((m): m is Partial<ProductMdx> => m !== null);
}

View File

@@ -39,23 +39,42 @@ export async function getPageBySlug(slug: string, locale: string): Promise<PageM
export async function getAllPages(locale: string): Promise<PageMdx[]> { export async function getAllPages(locale: string): Promise<PageMdx[]> {
const pagesDir = path.join(process.cwd(), 'data', 'pages', locale); const pagesDir = path.join(process.cwd(), 'data', 'pages', locale);
if (!fs.existsSync(pagesDir)) return []; if (!fs.existsSync(pagesDir)) return [];
const files = fs.readdirSync(pagesDir); const files = fs.readdirSync(pagesDir);
const pages = await Promise.all( const pages = await Promise.all(
files files
.filter(file => file.endsWith('.mdx')) .filter((file) => file.endsWith('.mdx'))
.map(file => { .map((file) => {
const fileSlug = file.replace(/\.mdx$/, ''); const fileSlug = file.replace(/\.mdx$/, '');
const filePath = path.join(pagesDir, file); const filePath = path.join(pagesDir, file);
const fileContent = { content: fs.readFileSync(filePath, 'utf8') }; const fileContent = fs.readFileSync(filePath, 'utf8');
const { data, content } = matter(fileContent.content); const { data, content } = matter(fileContent);
return { return {
slug: fileSlug, slug: fileSlug,
frontmatter: data as PageFrontmatter, frontmatter: data as PageFrontmatter,
content, content,
}; };
}) }),
); );
return pages.filter((p): p is PageMdx => p !== null); return pages.filter((p): p is PageMdx => p !== null);
} }
export async function getAllPagesMetadata(locale: string): Promise<Partial<PageMdx>[]> {
const pagesDir = path.join(process.cwd(), 'data', 'pages', locale);
if (!fs.existsSync(pagesDir)) return [];
const files = fs.readdirSync(pagesDir);
return files
.filter((file) => file.endsWith('.mdx'))
.map((file) => {
const fileSlug = file.replace(/\.mdx$/, '');
const filePath = path.join(pagesDir, file);
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data } = matter(fileContent);
return {
slug: fileSlug,
frontmatter: data as PageFrontmatter,
};
});
}