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
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:
@@ -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(),
|
||||||
|
|||||||
38
lib/blog.ts
38
lib/blog.ts
@@ -44,8 +44,8 @@ export async function getAllPosts(locale: string): Promise<PostMdx[]> {
|
|||||||
|
|
||||||
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 };
|
||||||
|
|||||||
76
lib/mdx.ts
76
lib/mdx.ts
@@ -18,6 +18,56 @@ 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);
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +134,7 @@ export async function getAllProductSlugs(locale: string): Promise<string[]> {
|
|||||||
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);
|
||||||
|
}
|
||||||
|
|||||||
29
lib/pages.ts
29
lib/pages.ts
@@ -43,19 +43,38 @@ export async function getAllPages(locale: string): Promise<PageMdx[]> {
|
|||||||
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,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user