diff --git a/src/lib/slugify.test.ts b/src/lib/slugify.test.ts new file mode 100644 index 0000000..db20ae4 --- /dev/null +++ b/src/lib/slugify.test.ts @@ -0,0 +1,36 @@ +import { describe, it, expect } from 'vitest'; +import { slugify } from './slugify'; + +describe('slugify', () => { + it('converts basic string with spaces', () => { + expect(slugify('Hello World')).toBe('hello-world'); + }); + + it('strips accented characters', () => { + expect(slugify('Café über')).toBe('cafe-uber'); + }); + + it('handles multiple spaces', () => { + expect(slugify('multiple spaces here')).toBe('multiple-spaces-here'); + }); + + it('replaces special characters with hyphens', () => { + expect(slugify('hello@world! How_are you?')).toBe('hello-world-how-are-you'); + }); + + it('returns already-clean slug unchanged (except lowercased)', () => { + expect(slugify('already-clean-slug')).toBe('already-clean-slug'); + }); + + it('returns empty string for empty input', () => { + expect(slugify('')).toBe(''); + }); + + it('returns empty string for all special chars', () => { + expect(slugify('!@#$%^&*()_+=[]{}|;:",.<>?/\\')).toBe(''); + }); + + it('handles numbers mixed with text', () => { + expect(slugify('Product 123 name!')).toBe('product-123-name'); + }); +}); diff --git a/src/lib/slugify.ts b/src/lib/slugify.ts new file mode 100644 index 0000000..2435e05 --- /dev/null +++ b/src/lib/slugify.ts @@ -0,0 +1,15 @@ +export function slugify(text: string): string { + // Normalize to NFD form to split accented letters + const normalized = text.normalize('NFD'); + // Remove diacritics (accents) + const withoutAccents = normalized.replace(/\p{Diacritic}+/gu, ''); + // Replace non-alphanumeric chars (including underscores and spaces) with hyphens + const replaced = withoutAccents.replace(/[^a-zA-Z0-9]+/g, '-'); + // Lowercase + const lower = replaced.toLowerCase(); + // Collapse multiple hyphens + const collapsed = lower.replace(/-+/g, '-'); + // Trim leading/trailing hyphens + const trimmed = collapsed.replace(/^-+|-+$/g, ''); + return trimmed; +}