Files
gh-yebot-rad-cc-plugins-plu…/commands/new-collection.md
2025-11-30 09:07:42 +08:00

9.8 KiB

Create New Astro Content Collection

This command guides you through creating a new content collection in an Astro project with proper schema definition, directory structure, and TypeScript integration.

Instructions

Follow these steps to create a new content collection:

1. Gather Requirements

Ask the user for:

  • Collection name (e.g., 'blog', 'docs', 'products', 'team')
  • Collection purpose (what type of content?)
  • Required fields and their types
  • Optional fields
  • File format preference (markdown, JSON, YAML, etc.)

2. Design Schema Fields

Based on the collection purpose, determine appropriate fields:

For blog/articles:

  • title (string, required)
  • description (string, required)
  • pubDate (date, required)
  • author (string, required)
  • tags (array of strings)
  • draft (boolean, default false)
  • image (object with url and alt)

For documentation:

  • title (string, required)
  • description (string, required)
  • sidebar (object with order, label)
  • lastUpdated (date, optional)
  • contributors (array of strings)

For products:

  • name (string, required)
  • description (string, required)
  • price (number, required)
  • category (enum)
  • features (array of strings)
  • inStock (boolean)
  • images (array of objects)

For team members:

  • name (string, required)
  • role (string, required)
  • bio (string, required)
  • avatar (string, required)
  • social (object with URLs)
  • startDate (date, required)

3. Locate or Create Config File

  1. Check if config exists:

    • Look for src/content/config.ts
    • Or src/content.config.ts
  2. If it doesn't exist, create src/content/config.ts

  3. Ensure imports are present:

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

4. Define Collection Schema

Create the collection definition with appropriate Zod schema:

const [collectionName] = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/content/[collectionName]" }),
  schema: z.object({
    // Define fields based on requirements
  })
});

Use appropriate Zod types:

  • z.string() - Text fields
  • z.number() - Numeric fields
  • z.boolean() - True/false fields
  • z.coerce.date() - Date fields (with auto-conversion)
  • z.array(z.string()) - Arrays
  • z.object({...}) - Nested objects
  • z.enum(['value1', 'value2']) - Fixed options
  • Add .optional() for optional fields
  • Add .default(value) for default values

5. Add to Collections Export

Update the export statement:

export const collections = {
  // existing collections...
  [collectionName]: [collectionName]
};

6. Create Collection Directory

Create the directory structure:

mkdir -p src/content/[collectionName]

7. Create Sample Content File

Create a sample file to test the schema:

For markdown: src/content/[collectionName]/sample.md

For JSON: src/content/[collectionName]/sample.json

For YAML: src/content/[collectionName]/sample.yml

Include frontmatter/data that matches the schema.

8. Validate Schema

  1. Start the dev server: npm run dev
  2. Check for TypeScript errors
  3. Verify the sample file validates correctly
  4. Check .astro/types.d.ts for generated types

9. Document Usage

Create a comment block in the config explaining the collection:

/**
 * [Collection Name] Collection
 *
 * Purpose: [Description]
 *
 * Required fields:
 * - field1: description
 * - field2: description
 *
 * Optional fields:
 * - field3: description
 */
const [collectionName] = defineCollection({...});

10. Provide Query Examples

Show the user how to query the new collection:

import { getCollection, getEntry } from 'astro:content';

// Get all items
const items = await getCollection('[collectionName]');

// Get published items (if applicable)
const published = await getCollection('[collectionName]', ({ data }) => {
  return !data.draft;
});

// Get single item
const item = await getEntry('[collectionName]', 'slug');

Complete Example

Collection Config (src/content/config.ts):

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

/**
 * Blog Posts Collection
 *
 * Stores blog articles and tutorials
 */
const blog = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
  schema: z.object({
    title: z.string().max(80),
    description: z.string().max(160),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    author: z.string(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    featured: z.boolean().default(false),
    image: z.object({
      url: z.string(),
      alt: z.string()
    }).optional()
  })
});

/**
 * Documentation Collection
 *
 * Technical documentation and guides
 */
const docs = defineCollection({
  loader: glob({ pattern: "**/*.mdx", base: "./src/content/docs" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    sidebar: z.object({
      order: z.number(),
      label: z.string().optional(),
      hidden: z.boolean().default(false)
    }).optional(),
    lastUpdated: z.coerce.date().optional(),
    contributors: z.array(z.string()).default([])
  })
});

export const collections = { blog, docs };

Sample Blog Post (src/content/blog/first-post.md):

---
title: 'My First Post'
description: 'This is my first blog post'
pubDate: 2024-01-15
author: 'John Doe'
tags: ['intro', 'blogging']
draft: false
featured: true
---

# Hello World

This is my first post!

Definition of Done

  • Collection requirements gathered from user
  • Schema fields identified and typed
  • Config file located or created
  • Collection definition added with proper schema
  • Loader configured for correct file pattern
  • Zod types match requirements
  • Collection exported in collections object
  • Collection directory created
  • Sample content file created
  • Sample file validates against schema
  • Dev server starts without errors
  • TypeScript types generated correctly
  • Documentation comments added
  • Query examples provided to user

Schema Best Practices

String Validation

title: z.string().min(1).max(100)
email: z.string().email()
url: z.string().url()
slug: z.string().regex(/^[a-z0-9-]+$/)

Number Validation

price: z.number().positive()
age: z.number().int().min(0).max(120)
rating: z.number().min(1).max(5)

Dates

pubDate: z.coerce.date()  // Converts strings to Date
deadline: z.date()         // Requires Date object

Arrays

tags: z.array(z.string()).min(1)      // At least one
images: z.array(z.string()).max(10)   // Maximum 10
authors: z.array(z.string()).default([])

Enums

status: z.enum(['draft', 'published', 'archived'])
priority: z.enum(['low', 'medium', 'high'])

Optional Fields

subtitle: z.string().optional()
updatedDate: z.coerce.date().optional()

Default Values

draft: z.boolean().default(false)
views: z.number().default(0)
tags: z.array(z.string()).default([])

Nested Objects

author: z.object({
  name: z.string(),
  email: z.string().email(),
  url: z.string().url().optional()
})

Records (Dynamic Keys)

metadata: z.record(z.string())  // Any string keys
settings: z.record(z.boolean()) // Boolean values

Loader Patterns

Markdown Files

loader: glob({ pattern: "**/*.md", base: "./src/content/blog" })

MDX Files

loader: glob({ pattern: "**/*.mdx", base: "./src/content/docs" })

Multiple Formats

loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/mixed" })

JSON Files

loader: glob({ pattern: "**/*.json", base: "./src/content/data" })

YAML Files

loader: glob({ pattern: "**/*.{yml,yaml}", base: "./src/content/config" })

Single File

loader: file("src/data/settings.json")

Common Collection Types

Blog

const blog = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    author: z.string(),
    tags: z.array(z.string()),
    draft: z.boolean().default(false)
  })
});

Portfolio Projects

const projects = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/content/projects" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    technologies: z.array(z.string()),
    liveUrl: z.string().url().optional(),
    githubUrl: z.string().url().optional(),
    thumbnail: z.string(),
    date: z.coerce.date()
  })
});

Product Catalog

const products = defineCollection({
  loader: glob({ pattern: "**/*.json", base: "./src/content/products" }),
  schema: z.object({
    name: z.string(),
    description: z.string(),
    price: z.number().positive(),
    category: z.enum(['software', 'hardware', 'service']),
    features: z.array(z.string()),
    inStock: z.boolean()
  })
});

Error Prevention

  • Always use z.coerce.date() for date strings
  • Provide defaults for booleans and arrays
  • Use enums for fixed value sets
  • Validate URLs and emails with built-in validators
  • Test schema with sample content first
  • Restart dev server after schema changes
  • Export all collections in the collections object
  • Use consistent naming (kebab-case for directories)

Tips

  • Start with required fields, add optional later
  • Use TypeScript comments for documentation
  • Create sample content immediately
  • Test validation with invalid data
  • Consider future needs when designing schema
  • Use subdirectories for organization
  • Keep schemas focused and specific
  • Reuse common patterns across collections