main
1import { defineCollection } from "astro:content";
2import { glob } from "astro/loaders";
3import { z } from "astro/zod";
4import { slug as slugify } from "github-slugger";
5import type { Locale } from "intlayer";
6
7import { SITE_CONFIG } from "@/config.ts";
8
9const typCollection = (base: string) =>
10 defineCollection({
11 loader: glob({
12 pattern: ["**/[^_]*.{typ,typst}", "!**/_*/**"],
13 base: `./src/contents/${base}`,
14 generateId: ({ entry, data }) => {
15 const slugFromData = data.slug as string | undefined;
16 const filename = entry.split("/").at(-1);
17 const isFlatEntry =
18 filename.split(".").length > 2 &&
19 SITE_CONFIG.locales.indexOf(filename.split(".").at(-2) as Locale) !==
20 -1;
21 const locale = filename.split(".").at(-2);
22 const slugFromEntry = isFlatEntry
23 ? filename.split(".").slice(0, -2).join("-")
24 : entry.split("/").at(-2);
25 return `${locale}/${slugify(slugFromData || slugFromEntry)}`;
26 },
27 }),
28 schema: z.object({
29 title: z.string(),
30 slug: z.string().optional(),
31 author: z.string().default(SITE_CONFIG.author),
32 desc: z.any().optional(),
33 date: z.string().transform((str) => {
34 const match = str.match(
35 /datetime\(\s*year:\s*(\d+),?\s*month:\s*(\d+),?\s*day:\s*(\d+)(?:,?\s*hour:\s*(\d+))?(?:,?\s*minute:\s*(\d+))?(?:,?\s*second:\s*(\d+))?,?\s*\)/,
36 );
37 if (match) {
38 const year = parseInt(match[1], 10);
39 const month = parseInt(match[2], 10);
40 const day = parseInt(match[3], 10);
41 const hour = match[4] ? parseInt(match[4], 10) : 0;
42 const minute = match[5] ? parseInt(match[5], 10) : 0;
43 const second = match[6] ? parseInt(match[6], 10) : 0;
44
45 return Temporal.ZonedDateTime.from({
46 year,
47 month,
48 day,
49 hour,
50 minute,
51 second,
52 timeZone: SITE_CONFIG.timeZone,
53 }).toString();
54 }
55 return z.NEVER;
56 }),
57 license: z
58 .xor([
59 z.object({
60 name: z.string(),
61 link: z.url(),
62 }),
63 z.string(),
64 ])
65 .default({
66 name: "CC BY-NC-SA 4.0",
67 link: "https://creativecommons.org/licenses/by-nc-sa/4.0/",
68 }),
69 }),
70 });
71
72export const collections = {
73 posts: typCollection("posts"),
74};