42 lines
1.2 KiB
TypeScript
42 lines
1.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Generates a URL-safe slug from text
|
||
|
|
* @param text - Text to convert to slug
|
||
|
|
* @returns URL-safe slug (lowercase, hyphenated)
|
||
|
|
*/
|
||
|
|
export function slugify(text: string): string {
|
||
|
|
return text
|
||
|
|
.normalize('NFD') // Normalize unicode
|
||
|
|
.replace(/[\u0300-\u036f]/g, '') // Remove diacritics
|
||
|
|
.toLowerCase()
|
||
|
|
.trim()
|
||
|
|
.replace(/[^a-z0-9\s-]/g, '') // Remove non-alphanumeric except spaces and hyphens
|
||
|
|
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||
|
|
.replace(/-+/g, '-') // Collapse multiple hyphens
|
||
|
|
.replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generates a unique slug by appending a suffix if needed
|
||
|
|
* @param text - Text to convert to slug
|
||
|
|
* @param existingSlugs - Array of existing slugs to avoid collisions
|
||
|
|
* @returns Unique URL-safe slug
|
||
|
|
*/
|
||
|
|
export function uniqueSlugify(text: string, existingSlugs: string[]): string {
|
||
|
|
const baseSlug = slugify(text)
|
||
|
|
|
||
|
|
if (!existingSlugs.includes(baseSlug)) {
|
||
|
|
return baseSlug
|
||
|
|
}
|
||
|
|
|
||
|
|
// Append incrementing numbers until unique
|
||
|
|
let counter = 1
|
||
|
|
let slug = `${baseSlug}-${counter}`
|
||
|
|
|
||
|
|
while (existingSlugs.includes(slug)) {
|
||
|
|
counter++
|
||
|
|
slug = `${baseSlug}-${counter}`
|
||
|
|
}
|
||
|
|
|
||
|
|
return slug
|
||
|
|
}
|