refactor(blog): extract blog utilities into shared module
Move common blog functionality like post filtering, sorting and data extraction into a centralized utils module. Replace Astro.glob with import.meta.glob for better performance. Update all blog components and pages to use the new utilities.
This commit is contained in:
207
src/utils/blog-utils.ts
Normal file
207
src/utils/blog-utils.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Blog utility functions
|
||||
* This file contains common functions used across blog components
|
||||
*/
|
||||
|
||||
import { type BlogPost, type Lang } from '@/types';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
/**
|
||||
* Get blog posts based on language
|
||||
* @param lang - Current language
|
||||
* @param postsGlob - Object containing imported posts from glob
|
||||
* @returns Processed blog posts array
|
||||
*/
|
||||
export async function getBlogPosts(lang: Lang, postsGlob: Record<string, any>): Promise<BlogPost[]> {
|
||||
// Get posts based on language
|
||||
let allPosts = [];
|
||||
|
||||
if (lang === 'zh') {
|
||||
allPosts = postsGlob.zh;
|
||||
} else {
|
||||
allPosts = postsGlob.en;
|
||||
}
|
||||
|
||||
// Process blog post data
|
||||
const posts: BlogPost[] = allPosts.map((post: any) => {
|
||||
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||
|
||||
// Default image if not specified in frontmatter
|
||||
const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center";
|
||||
|
||||
return {
|
||||
title: post.frontmatter.title,
|
||||
description: post.frontmatter.description || '',
|
||||
image: post.frontmatter.image || defaultImage,
|
||||
slug: slug,
|
||||
tags: post.frontmatter.tags || [],
|
||||
tagId: post.frontmatter.tagId || [],
|
||||
category: Array.isArray(post.frontmatter.category) ? post.frontmatter.category : post.frontmatter.category ? [post.frontmatter.category] : [],
|
||||
categoryId: Array.isArray(post.frontmatter.categoryId) ? post.frontmatter.categoryId : post.frontmatter.categoryId ? [post.frontmatter.categoryId] : [],
|
||||
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||
readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5 min read',
|
||||
url: post.url || '',
|
||||
};
|
||||
});
|
||||
|
||||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort blog posts by date (newest first)
|
||||
* @param posts - Array of blog posts
|
||||
* @returns Sorted array of blog posts
|
||||
*/
|
||||
export function sortPostsByDate(posts: BlogPost[]): BlogPost[] {
|
||||
return posts
|
||||
.filter(post => post.date) // Filter out posts without dates
|
||||
.sort((a, b) => {
|
||||
const dateA = new Date(a.date).getTime();
|
||||
const dateB = new Date(b.date).getTime();
|
||||
return dateB - dateA; // Descending order, newest first
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter posts by category
|
||||
* @param posts - Array of blog posts
|
||||
* @param category - Category to filter by
|
||||
* @returns Filtered array of blog posts
|
||||
*/
|
||||
export function filterPostsByCategory(posts: BlogPost[], category: string): BlogPost[] {
|
||||
if (!category) return posts;
|
||||
|
||||
return posts.filter(post => {
|
||||
// First check if post contains the current category ID
|
||||
if (post.categoryId && Array.isArray(post.categoryId)) {
|
||||
return post.categoryId.some(catId =>
|
||||
catId.toLowerCase() === category.toLowerCase()
|
||||
);
|
||||
}
|
||||
// If no categoryId, check category
|
||||
else if (post.category && Array.isArray(post.category)) {
|
||||
return post.category.some(cat =>
|
||||
cat.toLowerCase() === category.toLowerCase()
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter posts by tag
|
||||
* @param posts - Array of blog posts
|
||||
* @param tag - Tag to filter by
|
||||
* @returns Filtered array of blog posts
|
||||
*/
|
||||
export function filterPostsByTag(posts: BlogPost[], tag: string): BlogPost[] {
|
||||
if (!tag) return posts;
|
||||
|
||||
return posts.filter(post => {
|
||||
// First check if post contains the current tag ID
|
||||
if (post.tagId && Array.isArray(post.tagId)) {
|
||||
return post.tagId.some(tagId =>
|
||||
tagId.toLowerCase() === tag.toLowerCase()
|
||||
);
|
||||
}
|
||||
// If no tagId, check tags
|
||||
else if (post.tags && Array.isArray(post.tags)) {
|
||||
return post.tags.some(postTag =>
|
||||
postTag.toLowerCase() === tag.toLowerCase()
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract categories from posts
|
||||
* @param posts - Array of blog posts
|
||||
* @returns Map of category names to category IDs
|
||||
*/
|
||||
export function extractCategories(posts: any[]): Map<string, string> {
|
||||
const allCategories = new Set<string>();
|
||||
const categoryMap = new Map<string, string>(); // Map of category name to ID
|
||||
|
||||
posts.forEach(post => {
|
||||
// Process categories
|
||||
if (post.frontmatter?.category) {
|
||||
const categories = Array.isArray(post.frontmatter.category)
|
||||
? post.frontmatter.category
|
||||
: [post.frontmatter.category];
|
||||
|
||||
const categoryIds = Array.isArray(post.frontmatter.categoryId)
|
||||
? post.frontmatter.categoryId
|
||||
: post.frontmatter.categoryId ? [post.frontmatter.categoryId] : [];
|
||||
|
||||
// If categoryId exists, use mapping between categoryId and category
|
||||
if (categoryIds.length > 0 && categoryIds.length === categories.length) {
|
||||
categories.forEach((cat: string, index: number) => {
|
||||
if (cat && categoryIds[index]) {
|
||||
allCategories.add(cat);
|
||||
categoryMap.set(cat, categoryIds[index]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// If no categoryId or lengths don't match, use category name as ID
|
||||
categories.forEach((cat: string) => {
|
||||
if (cat) {
|
||||
allCategories.add(cat);
|
||||
categoryMap.set(cat, cat.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return categoryMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract tags from posts
|
||||
* @param posts - Array of blog posts
|
||||
* @returns Map of tag names to tag IDs
|
||||
*/
|
||||
export function extractTags(posts: any[]): Map<string, string> {
|
||||
const allTags = new Set<string>();
|
||||
const tagMap = new Map<string, string>(); // Map of tag name to ID
|
||||
|
||||
posts.forEach(post => {
|
||||
// Process tags
|
||||
if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) {
|
||||
const tags = post.frontmatter.tags;
|
||||
const tagIds = Array.isArray(post.frontmatter.tagId)
|
||||
? post.frontmatter.tagId
|
||||
: post.frontmatter.tagId ? [post.frontmatter.tagId] : [];
|
||||
|
||||
// If tagId exists, use mapping between tagId and tag
|
||||
if (tagIds.length > 0 && tagIds.length === tags.length) {
|
||||
tags.forEach((tag: string, index: number) => {
|
||||
if (tag && tagIds[index]) {
|
||||
allTags.add(tag);
|
||||
tagMap.set(tag, tagIds[index]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// If no tagId or lengths don't match, use tag name as ID
|
||||
tags.forEach((tag: string) => {
|
||||
if (tag) {
|
||||
allTags.add(tag);
|
||||
tagMap.set(tag, tag.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return tagMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get base URL for blog based on language
|
||||
* @param lang - Current language
|
||||
* @returns Base URL string
|
||||
*/
|
||||
export function getBlogBaseUrl(lang: Lang): string {
|
||||
return lang === defaultLang ? '/blog' : `/${lang}/blog`;
|
||||
}
|
||||
Reference in New Issue
Block a user