@bernierllc/content-type-blog-post
Version:
Blog post content type with rich TipTap editor, SEO metadata, database storage, and web publishing
54 lines (46 loc) • 1.51 kB
text/typescript
/*
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license.
The client may use and modify this code *only within the scope of the project it was delivered for*.
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
*/
/**
* Validate slug format
* @param slug - Slug to validate
* @returns True if valid, false otherwise
*/
export function isValidSlug(slug: string): boolean {
return /^[a-z0-9-]+$/.test(slug);
}
/**
* Generate slug from title
* @param title - Title to convert to slug
* @returns Slug string
*/
export function generateSlugFromTitle(title: string): string {
return title
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '') // Remove special characters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
.replace(/^-+|-+$/g, ''); // Trim hyphens from start/end
}
/**
* Ensure slug is unique by appending counter if needed
* @param slug - Base slug
* @param existingSlugs - Array of existing slugs to check against
* @returns Unique slug
*/
export function ensureUniqueSlug(slug: string, existingSlugs: string[]): string {
if (!existingSlugs.includes(slug)) {
return slug;
}
let counter = 1;
let uniqueSlug = `${slug}-${counter}`;
while (existingSlugs.includes(uniqueSlug)) {
counter++;
uniqueSlug = `${slug}-${counter}`;
}
return uniqueSlug;
}