Skip to content

Commit f4bf439

Browse files
committed
Add zod schema to blog mdx types
1 parent 2e5e40a commit f4bf439

File tree

2 files changed

+44
-34
lines changed

2 files changed

+44
-34
lines changed

packages/web/docs/src/app/blog/blog-types.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
import type { StaticImageData } from 'next/image';
2-
import type { Author, AuthorId } from '../../authors';
1+
import { z } from 'zod';
2+
import { AuthorOrId, staticImageDataSchema } from '../../authors';
33
import type { MdxFile, PageMapItem } from '../../mdx-types';
44

5-
type OneOrMany<T> = T | T[];
5+
export const VideoPath = z
6+
.string()
7+
.regex(/^.+\.(webm|mp4)$/) as z.ZodType<`${string}.${'webm' | 'mp4'}`>;
68

7-
export interface BlogFrontmatter {
8-
authors: OneOrMany<AuthorId | Author>;
9-
title: string;
10-
date: string;
11-
tags: string | string[];
12-
featured?: boolean;
13-
image?: VideoPath | StaticImageData;
14-
thumbnail?: StaticImageData;
15-
description?: string;
16-
}
9+
export type VideoPath = z.infer<typeof VideoPath>;
10+
11+
export const BlogFrontmatter = z.object({
12+
authors: z.array(AuthorOrId),
13+
title: z.string(),
14+
date: z.string(),
15+
tags: z.union([z.string(), z.array(z.string())]),
16+
featured: z.boolean().optional(),
17+
image: z.union([VideoPath, staticImageDataSchema]).optional(),
18+
thumbnail: staticImageDataSchema.optional(),
19+
description: z.string().optional(),
20+
});
1721

18-
type VideoPath = `${string}.${'webm' | 'mp4'}`;
22+
export type BlogFrontmatter = z.infer<typeof BlogFrontmatter>;
1923

2024
export type BlogPostFile = Required<MdxFile<BlogFrontmatter>>;
2125

packages/web/docs/src/authors/index.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,34 @@ import type { StaticImageData } from 'next/image';
22
import { z } from 'zod';
33
import saihajPhoto from './saihaj.webp';
44

5-
export type Author =
6-
| {
7-
name: string;
8-
link: `https://${string}`;
9-
twitter?: string;
10-
github?: string;
11-
avatar: string | StaticImageData;
12-
}
13-
| {
14-
name: string;
15-
link: `https://${string}`;
16-
twitter?: string;
17-
github: string;
18-
// if the author has no avatar, we'll take it from GitHub
19-
avatar?: string | StaticImageData;
20-
};
21-
22-
export const Author = z.object({
5+
const commonAuthorFields = z.object({
236
name: z.string(),
247
link: z.string().url(),
258
twitter: z.string().optional(),
26-
github: z.string().optional(),
27-
avatar: z.union([z.string(), z.object({})]).optional(),
289
});
2910

11+
export const staticImageDataSchema = z.object({
12+
src: z.string(),
13+
}) as unknown as z.ZodType<StaticImageData>;
14+
15+
export const Author = z.intersection(
16+
commonAuthorFields,
17+
z.union([
18+
z.object({
19+
// if we have a GitHub handle, we don't require the avatar
20+
avatar: z.union([z.string(), staticImageDataSchema]),
21+
github: z.string().optional(),
22+
}),
23+
z.object({
24+
github: z.string(),
25+
// if the author has no avatar, we'll take it from GitHub
26+
avatar: z.union([z.string(), staticImageDataSchema]).optional(),
27+
}),
28+
]),
29+
);
30+
31+
export type Author = z.infer<typeof Author>;
32+
3033
export const authors = {
3134
kamil: {
3235
name: 'Kamil Kisiela',
@@ -266,3 +269,6 @@ export type AuthorId = keyof typeof authors;
266269
export const AuthorId = z.string().refine((val): val is AuthorId => val in authors, {
267270
message: `AuthorId must be one of: ${Object.keys(authors).join(', ')}`,
268271
});
272+
273+
export const AuthorOrId = z.union([AuthorId, Author]);
274+
export type AuthorOrId = z.infer<typeof AuthorOrId>;

0 commit comments

Comments
 (0)