Files
website/src/lib/server/slugify.ts
2026-10-24 02:52:18 +00:00

50 lines
1.3 KiB
TypeScript

import { db } from '#lib/server/db/index.js';
import { blogArticles, metaTables } from '#lib/server/db/schema/index.js';
import { eq } from 'drizzle-orm';
export function slugify(title: string, fallback = 'article'): string {
return (
title
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '') || fallback
);
}
export async function uniqueSlug(title: string): Promise<string> {
const base = slugify(title);
let slug = base;
let suffix = 2;
while (
(await db.select({ id: blogArticles.id }).from(blogArticles).where(eq(blogArticles.slug, slug)))
.length > 0
) {
slug = `${base}-${suffix}`;
suffix += 1;
}
return slug;
}
/** /lists/db is the database browser, so no list may use that slug. */
export const RESERVED_LIST_SLUGS = new Set(['db']);
export async function uniqueListSlug(name: string): Promise<string> {
const base = slugify(name, 'list');
let slug = RESERVED_LIST_SLUGS.has(base) ? `${base}-2` : base;
let suffix = RESERVED_LIST_SLUGS.has(base) ? 3 : 2;
while (
(await db.select({ id: metaTables.id }).from(metaTables).where(eq(metaTables.slug, slug)))
.length > 0
) {
slug = `${base}-${suffix++}`;
}
return slug;
}
export function validSlug(slug: string): boolean {
return slug.length <= 120 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug);
}