eratic / sites / blog / src / content.config.ts
326 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
import { baseEntry, themeOverride } from '@eratic/core';

/**
 * blog.eratic.ro has two kinds of entry that share one feed and one URL space.
 *
 *   posts/2026/a-walk-in-fog.mdx   → /2026/a-walk-in-fog/
 *   photos/2026/harbour.mdx        → /2026/harbour/
 *
 * They are separate collections because their frontmatter genuinely differs —
 * a photo set has a `photos` array and a post does not — but they render
 * through the same route and appear together, newest first, in the listing and
 * in both feeds. The `[^_]` in the glob means a filename starting with an
 * underscore is ignored, which is a quick way to park something.
 *
 * Two entries with the same year/slug across the two collections would produce
 * the same URL. Astro fails the build on duplicate routes, so that is caught
 * rather than silently shadowed.
 */

const posts = defineCollection({
	loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: './src/content/posts' }),
	schema: ({ image }) =>
		baseEntry.extend({
			/** article (default) · wide · bare — see docs/02-design-system.md */
			canvas: z.enum(['article', 'wide', 'bare']).default('article'),
			theme: themeOverride,
			/** Lead image. Also used for link previews. */
			cover: image().optional(),
			coverAlt: z.string().optional(),
			/** Show the cover at the top of the article as well as in previews. */
			showCover: z.boolean().default(true),
		}),
});

const photos = defineCollection({
	loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: './src/content/photos' }),
	schema: ({ image }) =>
		baseEntry.extend({
			// Photo sets default to the wider track; text still sits at the
			// reading measure inside it.
			canvas: z.enum(['article', 'wide', 'bare']).default('wide'),
			theme: themeOverride,
			photos: z
				.array(
					z.object({
						src: image(),
						alt: z.string().min(1, 'Every photograph needs alt text.'),
						caption: z.string().optional(),
					}),
				)
				.min(1, 'A photo set needs at least one photograph.'),
			/** Render camera settings under each frame. */
			exif: z.boolean().default(true),
			columns: z.union([z.literal(1), z.literal(2), z.literal(3)]).default(2),
			/** Where these were made. A place name, never coordinates. */
			place: z.string().optional(),
		}),
});

export const collections = { posts, photos };

static mirror of HEAD · about · clone: git clone https://git.makefuture.ro/eratic.git