mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
215 lines
6.4 KiB
TypeScript
215 lines
6.4 KiB
TypeScript
import { defineCollection, reference } from 'astro:content';
|
|
import { file, glob } from 'astro/loaders';
|
|
import { z } from 'astro/zod';
|
|
import { ComponentReferenceSchema } from './types/component-reference';
|
|
import { SUPPORTED_FRAMEWORKS } from './types/docs';
|
|
import { FeatureReferenceSchema } from './types/feature-reference';
|
|
import { MediaReferenceSchema } from './types/media-reference';
|
|
import { PresetReferenceSchema } from './types/preset-reference';
|
|
import { UtilReferenceSchema } from './types/util-reference';
|
|
import { defaultGitService } from './utils/gitService';
|
|
import { globWithParser } from './utils/globWithParser';
|
|
|
|
/**
|
|
* Extract date from filename in format: YYYY-MM-DD-slug.mdx
|
|
* Throws an error if the filename doesn't match the expected pattern
|
|
*/
|
|
export function extractDateFromFilename(id: string): Date {
|
|
const match = id.match(/^(\d{4})-(\d{2})-(\d{2})-/);
|
|
if (!match) {
|
|
throw new Error(`Filename "${id}" must follow format: YYYY-MM-DD-slug.mdx`);
|
|
}
|
|
|
|
const [, year, month, day] = match;
|
|
return new Date(`${year}-${month}-${day}`);
|
|
}
|
|
|
|
const blog = defineCollection({
|
|
// Load MDX files in the `src/content/blog/` directory.
|
|
loader: globWithParser({
|
|
base: './src/content/blog',
|
|
pattern: '**/*.mdx',
|
|
generateId: ({ entry }) => {
|
|
// Remove date prefix and extension from slug (e.g., "2022-07-08-first-post.mdx" -> "first-post")
|
|
return entry.replace(/^\d{4}-\d{2}-\d{2}-/, '').replace(/\.mdx$/, '');
|
|
},
|
|
parser: async (entry, originalEntry) => {
|
|
// Extract pubDate from original filename (before date prefix was removed)
|
|
const pubDate = extractDateFromFilename(originalEntry);
|
|
|
|
// Get updatedDate from git history (last modification date)
|
|
const filePath = `site/src/content/blog/${originalEntry}`;
|
|
const updatedDate = await defaultGitService.getLastModifiedDate(filePath);
|
|
|
|
// Return transformed entry with added fields
|
|
return {
|
|
...entry,
|
|
data: {
|
|
...entry.data,
|
|
pubDate,
|
|
...(updatedDate && updatedDate.getTime() !== pubDate.getTime() ? { updatedDate } : {}),
|
|
},
|
|
};
|
|
},
|
|
}),
|
|
// Type-check frontmatter using a schema
|
|
schema: ({ image }) =>
|
|
z.object({
|
|
title: z.string(),
|
|
description: z.string(),
|
|
pubDate: z.date(),
|
|
updatedDate: z.coerce.date().optional(),
|
|
authors: z.array(reference('authors')),
|
|
canonical: z.url().optional(),
|
|
devOnly: z.boolean().optional(), // only visible in development mode
|
|
ogTitle: z.string().optional(),
|
|
ogImage: image().or(z.url()).optional(),
|
|
twitterImage: image().or(z.url()).optional(),
|
|
}),
|
|
});
|
|
|
|
const docs = defineCollection({
|
|
loader: globWithParser({
|
|
base: './src/content/docs',
|
|
pattern: '**/*.mdx',
|
|
parser: async (entry, originalEntry) => {
|
|
// Get updatedDate from git history
|
|
const filePath = `site/src/content/docs/${originalEntry}`;
|
|
const updatedDate = await defaultGitService.getLastModifiedDate(filePath);
|
|
|
|
// Return transformed entry with added field if updatedDate exists
|
|
return {
|
|
...entry,
|
|
data: {
|
|
...entry.data,
|
|
...(updatedDate ? { updatedDate } : {}),
|
|
},
|
|
};
|
|
},
|
|
}),
|
|
schema: z.object({
|
|
title: z.string(),
|
|
description: z.string(),
|
|
updatedDate: z.coerce.date().optional(),
|
|
ogTitle: z.string().optional(),
|
|
frameworkTitle: z.partialRecord(z.enum(SUPPORTED_FRAMEWORKS as [string, ...string[]]), z.string()).optional(),
|
|
}),
|
|
});
|
|
|
|
// Release notes generated by the release workflow (raw git-cliff bullets),
|
|
// then rewritten into prose by the changelog-prose workflow. MDX lets the
|
|
// changelog use the same typography component mapping as the blog.
|
|
const changelog = defineCollection({
|
|
loader: glob({
|
|
base: './src/content/changelog',
|
|
pattern: '*.mdx',
|
|
generateId: ({ entry }) => entry.replace(/\.mdx$/, ''),
|
|
}),
|
|
schema: z.object({
|
|
description: z.string(),
|
|
date: z.coerce.date(),
|
|
version: z.string(),
|
|
prerelease: z.boolean(),
|
|
breaking: z.boolean(),
|
|
compareUrl: z.url(),
|
|
}),
|
|
});
|
|
|
|
const authors = defineCollection({
|
|
loader: file('./src/content/authors.json'),
|
|
schema: z.object({
|
|
name: z.string(),
|
|
shortName: z.string(),
|
|
bio: z.string().optional(),
|
|
avatar: z.string().optional(),
|
|
socialLinks: z
|
|
.object({
|
|
x: z.string().optional(),
|
|
bluesky: z.string().optional(),
|
|
mastodon: z.string().optional(),
|
|
github: z.string().optional(),
|
|
linkedin: z.string().optional(),
|
|
website: z.string().optional(),
|
|
})
|
|
.optional(),
|
|
}),
|
|
});
|
|
|
|
const componentReference = defineCollection({
|
|
loader: glob({
|
|
pattern: '*.json',
|
|
base: './src/content/generated-component-reference',
|
|
}),
|
|
schema: ComponentReferenceSchema,
|
|
});
|
|
|
|
const utilReference = defineCollection({
|
|
loader: glob({
|
|
pattern: '*.json',
|
|
base: './src/content/generated-util-reference',
|
|
}),
|
|
schema: UtilReferenceSchema,
|
|
});
|
|
|
|
const featureReference = defineCollection({
|
|
loader: glob({
|
|
pattern: '*.json',
|
|
base: './src/content/generated-feature-reference',
|
|
}),
|
|
schema: FeatureReferenceSchema,
|
|
});
|
|
|
|
const mediaReference = defineCollection({
|
|
loader: glob({
|
|
pattern: '*.json',
|
|
base: './src/content/generated-media-reference',
|
|
}),
|
|
schema: MediaReferenceSchema,
|
|
});
|
|
|
|
const presetReference = defineCollection({
|
|
loader: glob({
|
|
pattern: '*.json',
|
|
base: './src/content/generated-preset-reference',
|
|
}),
|
|
schema: PresetReferenceSchema,
|
|
});
|
|
|
|
const ejectedSkins = defineCollection({
|
|
loader: file('./src/content/ejected-skins.json'),
|
|
schema: z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
platform: z.enum(['html', 'react']),
|
|
style: z.enum(['css', 'tailwind']),
|
|
html: z.string().optional(),
|
|
tsx: z.record(z.string(), z.string()).optional(),
|
|
jsx: z.record(z.string(), z.string()).optional(),
|
|
css: z.string().optional(),
|
|
}),
|
|
});
|
|
|
|
// Media subpaths that ship a CDN build, generated by scripts/build-cdn-manifest.ts.
|
|
// Each entry's id is a media subpath (e.g. `hlsjs-video`). Used by the installation
|
|
// guide to hide the CDN install option for renderers with no CDN bundle.
|
|
const cdnMedia = defineCollection({
|
|
loader: file('./src/content/cdn-media.json'),
|
|
schema: z.object({
|
|
id: z.string(),
|
|
}),
|
|
});
|
|
|
|
export const collections = {
|
|
blog,
|
|
docs,
|
|
changelog,
|
|
authors,
|
|
componentReference,
|
|
utilReference,
|
|
featureReference,
|
|
mediaReference,
|
|
presetReference,
|
|
ejectedSkins,
|
|
cdnMedia,
|
|
};
|