Refactor for i18n, CMS integration, and project slugs; enhance admin & analytics
Co-authored-by: dennis <dennis@konkol.net>
This commit is contained in:
30
lib/slug.ts
Normal file
30
lib/slug.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export function slugify(input: string): string {
|
||||
return input
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/['"]/g, "")
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "");
|
||||
}
|
||||
|
||||
export async function generateUniqueSlug(opts: {
|
||||
base: string;
|
||||
isTaken: (slug: string) => Promise<boolean>;
|
||||
maxAttempts?: number;
|
||||
}): Promise<string> {
|
||||
const maxAttempts = opts.maxAttempts ?? 50;
|
||||
const normalizedBase = slugify(opts.base) || "item";
|
||||
|
||||
let candidate = normalizedBase;
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
// First try the base, then base-2, base-3, ...
|
||||
candidate = i === 0 ? normalizedBase : `${normalizedBase}-${i + 1}`;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const taken = await opts.isTaken(candidate);
|
||||
if (!taken) return candidate;
|
||||
}
|
||||
|
||||
// Last resort: append timestamp to avoid collisions
|
||||
return `${normalizedBase}-${Date.now()}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user