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

426 lines
9.8 KiB
Markdown

# 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:
```typescript
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
```
### 4. Define Collection Schema
Create the collection definition with appropriate Zod schema:
```typescript
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:
```typescript
export const collections = {
// existing collections...
[collectionName]: [collectionName]
};
```
### 6. Create Collection Directory
Create the directory structure:
```bash
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:
```typescript
/**
* [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:
```typescript
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`):
```typescript
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`):
```markdown
---
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
```typescript
title: z.string().min(1).max(100)
email: z.string().email()
url: z.string().url()
slug: z.string().regex(/^[a-z0-9-]+$/)
```
### Number Validation
```typescript
price: z.number().positive()
age: z.number().int().min(0).max(120)
rating: z.number().min(1).max(5)
```
### Dates
```typescript
pubDate: z.coerce.date() // Converts strings to Date
deadline: z.date() // Requires Date object
```
### Arrays
```typescript
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
```typescript
status: z.enum(['draft', 'published', 'archived'])
priority: z.enum(['low', 'medium', 'high'])
```
### Optional Fields
```typescript
subtitle: z.string().optional()
updatedDate: z.coerce.date().optional()
```
### Default Values
```typescript
draft: z.boolean().default(false)
views: z.number().default(0)
tags: z.array(z.string()).default([])
```
### Nested Objects
```typescript
author: z.object({
name: z.string(),
email: z.string().email(),
url: z.string().url().optional()
})
```
### Records (Dynamic Keys)
```typescript
metadata: z.record(z.string()) // Any string keys
settings: z.record(z.boolean()) // Boolean values
```
## Loader Patterns
### Markdown Files
```typescript
loader: glob({ pattern: "**/*.md", base: "./src/content/blog" })
```
### MDX Files
```typescript
loader: glob({ pattern: "**/*.mdx", base: "./src/content/docs" })
```
### Multiple Formats
```typescript
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/mixed" })
```
### JSON Files
```typescript
loader: glob({ pattern: "**/*.json", base: "./src/content/data" })
```
### YAML Files
```typescript
loader: glob({ pattern: "**/*.{yml,yaml}", base: "./src/content/config" })
```
### Single File
```typescript
loader: file("src/data/settings.json")
```
## Common Collection Types
### Blog
```typescript
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
```typescript
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
```typescript
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