Skip to content

Commit

Permalink
feat: z.string().emoji() (#2045)
Browse files Browse the repository at this point in the history
* z.string().emoji()

* test emojis mixed with other chars
  • Loading branch information
joseph-lozano committed Feb 16, 2023
1 parent e693919 commit c244fb6
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 0 deletions.
1 change: 1 addition & 0 deletions deno/lib/ZodError.ts
Expand Up @@ -91,6 +91,7 @@ export interface ZodInvalidDateIssue extends ZodIssueBase {
export type StringValidation =
| "email"
| "url"
| "emoji"
| "uuid"
| "regex"
| "cuid"
Expand Down
10 changes: 10 additions & 0 deletions deno/lib/__tests__/string.test.ts
Expand Up @@ -129,6 +129,16 @@ test("url error overrides", () => {
}
});

test("emoji validations", () => {
const emoji = z.string().emoji();
try {
emoji.parse("🍺👩‍🚀🫡");
emoji.parse("💚 💙 💜 💛 ❤️");
expect(() => emoji.parse(":-)")).toThrow();
expect(() => emoji.parse("😀 is an emoji")).toThrow()
} catch (err) {}
});

test("uuid", () => {
const uuid = z.string().uuid("custom error");
uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0");
Expand Down
22 changes: 22 additions & 0 deletions deno/lib/types.ts
Expand Up @@ -492,6 +492,7 @@ export type ZodStringCheck =
| { kind: "length"; value: number; message?: string }
| { kind: "email"; message?: string }
| { kind: "url"; message?: string }
| { kind: "emoji"; message?: string }
| { kind: "uuid"; message?: string }
| { kind: "cuid"; message?: string }
| { kind: "cuid2"; message?: string }
Expand Down Expand Up @@ -526,6 +527,11 @@ const uuidRegex =
const emailRegex =
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|([^-]([a-zA-Z0-9-]*\.)+[a-zA-Z]{2,}))$/;

// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression

const emojiRegex =
/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff])[\ufe0e\ufe0f]?(?:[\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0]|\ud83c[\udffb-\udfff])?(?:\u200d(?:[^\ud800-\udfff]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff])[\ufe0e\ufe0f]?(?:[\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0]|\ud83c[\udffb-\udfff])?)*/;

// interface IsDateStringOptions extends StringDateOptions {
/**
* Match any configuration
Expand Down Expand Up @@ -653,6 +659,16 @@ export class ZodString extends ZodType<string, ZodStringDef> {
});
status.dirty();
}
} else if (check.kind === "emoji") {
if (!emojiRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "emoji",
code: ZodIssueCode.invalid_string,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "uuid") {
if (!uuidRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
Expand Down Expand Up @@ -773,6 +789,9 @@ export class ZodString extends ZodType<string, ZodStringDef> {
url(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) });
}
emoji(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) });
}
uuid(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) });
}
Expand Down Expand Up @@ -879,6 +898,9 @@ export class ZodString extends ZodType<string, ZodStringDef> {
get isURL() {
return !!this._def.checks.find((ch) => ch.kind === "url");
}
get isEmoji() {
return !!this._def.checks.find((ch) => ch.kind === "emoji");
}
get isUUID() {
return !!this._def.checks.find((ch) => ch.kind === "uuid");
}
Expand Down
1 change: 1 addition & 0 deletions src/ZodError.ts
Expand Up @@ -91,6 +91,7 @@ export interface ZodInvalidDateIssue extends ZodIssueBase {
export type StringValidation =
| "email"
| "url"
| "emoji"
| "uuid"
| "regex"
| "cuid"
Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/string.test.ts
Expand Up @@ -128,6 +128,16 @@ test("url error overrides", () => {
}
});

test("emoji validations", () => {
const emoji = z.string().emoji();
try {
emoji.parse("🍺👩‍🚀🫡");
emoji.parse("💚 💙 💜 💛 ❤️");
expect(() => emoji.parse(":-)")).toThrow();
expect(() => emoji.parse("😀 is an emoji")).toThrow()
} catch (err) {}
});

test("uuid", () => {
const uuid = z.string().uuid("custom error");
uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0");
Expand Down
22 changes: 22 additions & 0 deletions src/types.ts
Expand Up @@ -492,6 +492,7 @@ export type ZodStringCheck =
| { kind: "length"; value: number; message?: string }
| { kind: "email"; message?: string }
| { kind: "url"; message?: string }
| { kind: "emoji"; message?: string }
| { kind: "uuid"; message?: string }
| { kind: "cuid"; message?: string }
| { kind: "cuid2"; message?: string }
Expand Down Expand Up @@ -526,6 +527,11 @@ const uuidRegex =
const emailRegex =
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|([^-]([a-zA-Z0-9-]*\.)+[a-zA-Z]{2,}))$/;

// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression

const emojiRegex =
/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff])[\ufe0e\ufe0f]?(?:[\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0]|\ud83c[\udffb-\udfff])?(?:\u200d(?:[^\ud800-\udfff]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff])[\ufe0e\ufe0f]?(?:[\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0]|\ud83c[\udffb-\udfff])?)*/;

// interface IsDateStringOptions extends StringDateOptions {
/**
* Match any configuration
Expand Down Expand Up @@ -653,6 +659,16 @@ export class ZodString extends ZodType<string, ZodStringDef> {
});
status.dirty();
}
} else if (check.kind === "emoji") {
if (!emojiRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "emoji",
code: ZodIssueCode.invalid_string,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "uuid") {
if (!uuidRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
Expand Down Expand Up @@ -773,6 +789,9 @@ export class ZodString extends ZodType<string, ZodStringDef> {
url(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) });
}
emoji(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) });
}
uuid(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) });
}
Expand Down Expand Up @@ -879,6 +898,9 @@ export class ZodString extends ZodType<string, ZodStringDef> {
get isURL() {
return !!this._def.checks.find((ch) => ch.kind === "url");
}
get isEmoji() {
return !!this._def.checks.find((ch) => ch.kind === "emoji");
}
get isUUID() {
return !!this._def.checks.find((ch) => ch.kind === "uuid");
}
Expand Down

0 comments on commit c244fb6

Please sign in to comment.