Skip to content

Commit 3de39ee

Browse files
committed
Implement slugify
1 parent 162fe29 commit 3de39ee

File tree

6 files changed

+37
-6
lines changed

6 files changed

+37
-6
lines changed

packages/zod/src/v4/classic/checks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ export {
2727
_trim as trim,
2828
_toLowerCase as toLowerCase,
2929
_toUpperCase as toUpperCase,
30+
_slugify as slugify,
3031
type $RefinementCtx as RefinementCtx,
3132
} from "../core/index.js";

packages/zod/src/v4/classic/schemas.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export interface _ZodString<T extends core.$ZodStringInternals<unknown> = core.$
252252
normalize(form?: "NFC" | "NFD" | "NFKC" | "NFKD" | (string & {})): this;
253253
toLowerCase(): this;
254254
toUpperCase(): this;
255+
slugify(): this;
255256
}
256257

257258
/** @internal */
@@ -281,6 +282,7 @@ export const _ZodString: core.$constructor<_ZodString> = /*@__PURE__*/ core.$con
281282
inst.normalize = (...args) => inst.check(checks.normalize(...args));
282283
inst.toLowerCase = () => inst.check(checks.toLowerCase());
283284
inst.toUpperCase = () => inst.check(checks.toUpperCase());
285+
inst.slugify = () => inst.check(checks.slugify());
284286
});
285287

286288
export interface ZodString extends _ZodString<core.$ZodStringInternals<string>> {

packages/zod/src/v4/classic/tests/string.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,24 @@ test("lowerCase", () => {
803803
expect(z.string().toUpperCase().parse("asdf")).toEqual("ASDF");
804804
});
805805

806+
test("slugify", () => {
807+
expect(z.string().slugify().parse("Hello World")).toEqual("hello-world");
808+
expect(z.string().slugify().parse(" Hello World ")).toEqual("hello-world");
809+
expect(z.string().slugify().parse("Hello@World#123")).toEqual("helloworld123");
810+
expect(z.string().slugify().parse("Hello-World")).toEqual("hello-world");
811+
expect(z.string().slugify().parse("Hello_World")).toEqual("hello-world");
812+
expect(z.string().slugify().parse("---Hello---World---")).toEqual("hello-world");
813+
expect(z.string().slugify().parse("Hello World")).toEqual("hello-world");
814+
expect(z.string().slugify().parse("Hello!@#$%^&*()World")).toEqual("helloworld");
815+
816+
// can be used with check
817+
expect(z.string().check(z.slugify()).parse("Hello World")).toEqual("hello-world");
818+
819+
// can be chained with other methods
820+
expect(z.string().slugify().min(5).parse("Hello World")).toEqual("hello-world");
821+
expect(() => z.string().slugify().min(20).parse("Hello World")).toThrow();
822+
});
823+
806824
// test("IP validation", () => {
807825
// const ipSchema = z.string().ip();
808826

packages/zod/src/v4/core/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,10 @@ export function _toLowerCase(): checks.$ZodCheckOverwrite<string> {
10531053
export function _toUpperCase(): checks.$ZodCheckOverwrite<string> {
10541054
return _overwrite((input) => input.toUpperCase());
10551055
}
1056+
// slugify
1057+
export function _slugify(): checks.$ZodCheckOverwrite<string> {
1058+
return _overwrite((input) => util.slugify(input));
1059+
}
10561060

10571061
/////// collections ///////
10581062

packages/zod/src/v4/core/util.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,15 @@ export function esc(str: string): string {
351351
return JSON.stringify(str);
352352
}
353353

354+
export function slugify(input: string): string {
355+
return input
356+
.toLowerCase()
357+
.trim()
358+
.replace(/[^\w\s-]/g, "")
359+
.replace(/[\s_-]+/g, "-")
360+
.replace(/^-+|-+$/g, "");
361+
}
362+
354363
export const captureStackTrace: (targetObject: object, constructorOpt?: Function) => void = (
355364
"captureStackTrace" in Error ? Error.captureStackTrace : (..._args: any[]) => {}
356365
) as any;

play.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ import * as z from "zod";
22

33
z;
44

5-
const mac = z.stringFormat(
6-
"mac",
7-
/^(([0-9A-F]{2}([-:])[0-9A-F]{2}(\3[0-9A-F]{2}){4})|([0-9a-f]{2}([-:])[0-9a-f]{2}(\6[0-9a-f]{2}){4}))$/
8-
);
9-
10-
mac.parse("00:1A:2B:3C:4D:5E");
5+
// Test slugify
6+
console.log(z.string().slugify().parse("Hello World"));
7+
console.log(z.string().check(z.slugify()).parse("Hello World"));

0 commit comments

Comments
 (0)