66 lines
1.6 KiB
JavaScript
66 lines
1.6 KiB
JavaScript
|
|
/**
|
||
|
|
* Generate a URL-friendly slug from a collection name
|
||
|
|
*/
|
||
|
|
export function generateSlug(name) {
|
||
|
|
return name
|
||
|
|
.toLowerCase()
|
||
|
|
.trim()
|
||
|
|
// Replace spaces and special characters with hyphens
|
||
|
|
.replace(/[^a-z0-9]+/g, '-')
|
||
|
|
// Remove leading/trailing hyphens
|
||
|
|
.replace(/^-+|-+$/g, '')
|
||
|
|
// Limit length to 50 characters
|
||
|
|
.substring(0, 50)
|
||
|
|
// Remove trailing hyphen if truncation created one
|
||
|
|
.replace(/-+$/, '');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate a unique slug by checking against existing slugs
|
||
|
|
*/
|
||
|
|
export async function generateUniqueSlug(name, existingSlugs = []) {
|
||
|
|
const baseSlug = generateSlug(name);
|
||
|
|
|
||
|
|
// If base slug is unique, use it
|
||
|
|
if (!existingSlugs.includes(baseSlug)) {
|
||
|
|
return baseSlug;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Find the next available number suffix
|
||
|
|
let counter = 2;
|
||
|
|
let uniqueSlug = `${baseSlug}-${counter}`;
|
||
|
|
|
||
|
|
while (existingSlugs.includes(uniqueSlug)) {
|
||
|
|
counter++;
|
||
|
|
uniqueSlug = `${baseSlug}-${counter}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
return uniqueSlug;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validate a slug format
|
||
|
|
*/
|
||
|
|
export function isValidSlug(slug) {
|
||
|
|
if (!slug || typeof slug !== 'string') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Must be 1-50 characters, lowercase letters, numbers, and hyphens only
|
||
|
|
// Cannot start or end with hyphen
|
||
|
|
const slugRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
||
|
|
return slugRegex.test(slug) && slug.length <= 50;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert existing collection names to suggested slugs for migration
|
||
|
|
*/
|
||
|
|
export function suggestSlugForCollection(collection) {
|
||
|
|
const baseSlug = generateSlug(collection.name);
|
||
|
|
return {
|
||
|
|
id: collection.id,
|
||
|
|
name: collection.name,
|
||
|
|
currentSlug: collection.slug || null,
|
||
|
|
suggestedSlug: baseSlug
|
||
|
|
};
|
||
|
|
}
|