Skip to content

Instantly share code, notes, and snippets.

@jahirfiquitiva
Last active March 1, 2025 23:02
Show Gist options
  • Save jahirfiquitiva/215faf075ec057f473d25fe88882e708 to your computer and use it in GitHub Desktop.
Save jahirfiquitiva/215faf075ec057f473d25fe88882e708 to your computer and use it in GitHub Desktop.
Community events Astro site config
// 1. Import utilities from `astro:content`
import { defineCollection, z } from "astro:content";
// 2. Import loader(s)
import { glob, file, type Loader, type LoaderContext } from "astro/loaders";
// 3. Define your collection(s)
import fs from "node:fs/promises";
import path from "node:path";
import matter from "gray-matter";
// Define schemas
const speakerSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().optional(),
company: z.string(),
position: z.string(),
website: z.string().url().optional(),
photo: z.string(),
social: z
.object({
twitter: z.string().optional(),
github: z.string().optional(),
instagram: z.string().optional(),
linkedin: z.string().optional(),
bluesky: z.string().optional(),
mastodon: z.string().optional(),
threads: z.string().optional(),
})
.optional(),
});
const sponsorSchema = z.object({
id: z.string(),
name: z.string(),
logo: z.string(),
website: z.string().url().optional(),
description: z.string().optional(),
});
const sponsorCategoryEnum = z.enum(["gold", "emerald", "silver", "community"]);
const sponsorsSchema = z.record(sponsorCategoryEnum, z.array(sponsorSchema));
const agendaItemSchema = z.object({
time: z.string(),
speaker: z.string(),
title: z.string(),
pitch: z.string(),
recordingUrl: z.string().url().optional(),
});
const agendaSchema = z.record(z.string(), z.array(agendaItemSchema));
const venueSchema = z.object({
title: z.string().optional(),
address: z.string().optional(),
city: z.string().optional(),
capacity: z.number().optional(),
});
// Base path for event data
const DATA_BASE_PATH = "src/data";
const eventEntrySchema = z.object({
speakers: z.array(speakerSchema).default([]),
sponsors: sponsorsSchema.default({}),
agenda: agendaSchema,
venue: z.object({
data: venueSchema,
content: z.string()
}).optional(),
});
// Helper function to load a JSON file
async function loadJsonFile(filePath: string) {
try {
const content = await fs.readFile(filePath, "utf-8");
return JSON.parse(content);
} catch (err) {
return null;
}
}
// Helper function to load a markdown file
async function loadMarkdownFile(filePath: string) {
try {
const content = await fs.readFile(filePath, "utf-8");
return matter(content);
} catch (err) {
return null;
}
}
type EventEntry = z.infer<typeof eventEntrySchema>;
// Helper function to process a directory and generate entries
async function processDirectory(dirPath: string, idPrefix: string = "") {
const entry: EventEntry = {
speakers: [],
sponsors: {},
agenda: {},
};
// Load speakers.json
const speakersData = await loadJsonFile(path.join(dirPath,'/', "speakers.json"));
if (speakersData) {
entry.speakers = speakersData || [];
}
// Load sponsors.json
const sponsorsData = await loadJsonFile(path.join(dirPath,'/', "sponsors.json"));
if (sponsorsData) {
entry.sponsors = sponsorsData || {};
}
// Load agenda.json
const agendaData = await loadJsonFile(path.join(dirPath,'/', "agenda.json"));
if (agendaData) {
entry.agenda = agendaData || {};
}
// Load venue.md
const venueData = await loadMarkdownFile(path.join(dirPath,'/', "venue.md"));
if (venueData) {
entry.venue = venueData;
}
return {
id: idPrefix,
...entry,
};
}
// Create a generic loader factory
function createEventLoader(eventType: "conf" | "express" | "meetup", hasMonths = false) {
return async () => {
const entries = [];
const typePath = path.join(DATA_BASE_PATH, eventType);
try {
const years = await fs.readdir(typePath);
for (const year of years) {
const yearPath = path.join(typePath, year);
const yearStat = await fs.stat(yearPath);
if (!yearStat.isDirectory()) {
continue;
}
if (!hasMonths) {
// For conf events, load directly from year directory
const yearEntry = await processDirectory(yearPath, `${year}`);
entries.push(yearEntry);
} else {
// For express and meetup events, load from year/month directories
try {
const months = await fs.readdir(yearPath);
for (const month of months) {
const monthPath = path.join(yearPath, month);
const monthStat = await fs.stat(monthPath);
if (monthStat.isDirectory()) {
const monthEntry = await processDirectory(monthPath, `${year}/${month}`);
entries.push(monthEntry);
}
}
} catch (err) {
// Year directory might not contain months or be readable, continue
}
}
}
} catch (err) {
// Type directory might not exist, continue
}
return entries;
};
}
// Define collection loaders using the factory
const confLoader = createEventLoader("conf", false);
const expressLoader = createEventLoader("express", true);
const meetupLoader = createEventLoader("meetup", true);
const eventCollectionSchema = eventEntrySchema.extend({
id: z.string(),
});
// Define collections
export const collections = {
conf: defineCollection({
schema: eventCollectionSchema,
loader: confLoader,
}),
express: defineCollection({
schema: eventCollectionSchema,
loader: expressLoader,
}),
meetup: defineCollection({
schema: eventCollectionSchema,
loader: meetupLoader,
}),
};
---
import Layout from "../layouts/layout.astro";
import { getCollection } from "astro:content";
const allConfs = await getCollection("conf");
const allExpress = await getCollection("express");
const allMeetups = await getCollection("meetup");
---
<Layout>
<section>
<h1>Conferences</h1>
<ul>
{
allConfs.map((conf) => (
<li>
<div>
<h2>{conf.id}</h2>
<h3>Speakers</h3>
<ul>
{conf.data.speakers.map((sp) => (
<li>{sp.name}</li>
))}
</ul>
<h3>Sponsors</h3>
<ul>
{Object.keys(conf.data.sponsors).map((category) => (
<li>
<div>
<h4>{category}</h4>
<ul>
{conf.data.sponsors[
category as keyof typeof conf.data.sponsors
]?.map((sponsor) => (
<li>{sponsor.name}</li>
))}
</ul>
</div>
</li>
))}
</ul>
</div>
</li>
))
}
</ul>
</section>
<section>
<h1>Express</h1>
<ul>
{
allExpress.map((conf) => (
<li>
<div>
<h2>{conf.id}</h2>
<h3>Speakers</h3>
<ul>
{conf.data.speakers.map((sp) => (
<li>{sp.name}</li>
))}
</ul>
<h3>Sponsors</h3>
<ul>
{Object.keys(conf.data.sponsors).map((category) => (
<li>
<div>
<h4>{category}</h4>
<ul>
{conf.data.sponsors[
category as keyof typeof conf.data.sponsors
]?.map((sponsor) => (
<li>{sponsor.name}</li>
))}
</ul>
</div>
</li>
))}
</ul>
</div>
</li>
))
}
</ul>
</section>
<section>
<h1>Meetups</h1>
<ul>
{
allMeetups.map((conf) => (
<li>
<div>
<h2>{conf.id}</h2>
<h3>Speakers</h3>
<ul>
{conf.data.speakers.map((sp) => (
<li>{sp.name}</li>
))}
</ul>
<h3>Sponsors</h3>
<ul>
{Object.keys(conf.data.sponsors).map((category) => (
<li>
<div>
<h4>{category}</h4>
<ul>
{conf.data.sponsors[
category as keyof typeof conf.data.sponsors
]?.map((sponsor) => (
<li>{sponsor.name}</li>
))}
</ul>
</div>
</li>
))}
</ul>
</div>
</li>
))
}
</ul>
</section>
</Layout>
[
{
"id": "fulanito",
"name": "Fulanito",
"company": "Company Co.",
"position":"Developer",
"photo": "./img/speakers/fulanito.png"
}
]
{
"gold": [{
"id":"company",
"logo": "./img/sponsors/company.png",
"name": "Company"
}],
"emerald": [{
"id":"company",
"logo": "./img/sponsors/company.png",
"name": "Company"
}]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment