Initial commit
This commit is contained in:
425
commands/new-collection.md
Normal file
425
commands/new-collection.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# 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
|
||||
182
commands/new-post.md
Normal file
182
commands/new-post.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Create New Astro Blog Post
|
||||
|
||||
This command guides you through creating a new blog post for an Astro site with proper frontmatter, content structure, and collection compliance.
|
||||
|
||||
## Instructions
|
||||
|
||||
Follow these steps to create a new blog post:
|
||||
|
||||
### 1. Gather Information
|
||||
|
||||
Ask the user for the following details (if not already provided):
|
||||
- Post title
|
||||
- Post description (for SEO)
|
||||
- Author name
|
||||
- Tags (comma-separated or array)
|
||||
- Whether to create a draft or published post
|
||||
- Target collection (default: 'blog')
|
||||
|
||||
### 2. Check Collection Schema
|
||||
|
||||
Before creating the post:
|
||||
|
||||
1. Look for the content collection configuration:
|
||||
- Check `src/content/config.ts`
|
||||
- Or check `src/content.config.ts`
|
||||
|
||||
2. Review the schema for the target collection (usually 'blog')
|
||||
|
||||
3. Note all required and optional fields
|
||||
|
||||
4. Identify the data types for each field
|
||||
|
||||
### 3. Generate Slug
|
||||
|
||||
Create a URL-friendly slug from the title:
|
||||
- Convert to lowercase
|
||||
- Replace spaces with hyphens
|
||||
- Remove special characters
|
||||
- Example: "My First Post!" → "my-first-post"
|
||||
|
||||
### 4. Determine File Location
|
||||
|
||||
Based on the project structure:
|
||||
- Collection-based: `src/content/blog/[slug].md`
|
||||
- Page-based: `src/pages/blog/[slug].md`
|
||||
|
||||
Check existing posts to determine the correct location.
|
||||
|
||||
### 5. Create Frontmatter
|
||||
|
||||
Build frontmatter matching the schema. Common fields:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: 'Post Title Here'
|
||||
description: 'SEO-friendly description'
|
||||
pubDate: 2024-01-15
|
||||
author: 'Author Name'
|
||||
tags: ['astro', 'blogging', 'tutorial']
|
||||
draft: false
|
||||
image:
|
||||
url: './images/cover.jpg'
|
||||
alt: 'Image description'
|
||||
---
|
||||
```
|
||||
|
||||
Adapt based on the actual schema found in step 2.
|
||||
|
||||
### 6. Create Starter Content
|
||||
|
||||
Add initial markdown content:
|
||||
|
||||
```markdown
|
||||
# Introduction
|
||||
|
||||
Start writing your post here...
|
||||
|
||||
## Main Section
|
||||
|
||||
Content goes here.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Wrap up your thoughts.
|
||||
```
|
||||
|
||||
### 7. Handle Images (Optional)
|
||||
|
||||
If the user wants to include images:
|
||||
|
||||
1. Create an `images/` directory next to the post (if it doesn't exist)
|
||||
2. Note the image paths in frontmatter
|
||||
3. Provide guidance on image placement
|
||||
|
||||
### 8. Write the File
|
||||
|
||||
Create the file with:
|
||||
- Complete frontmatter
|
||||
- Starter content structure
|
||||
- Proper formatting
|
||||
|
||||
### 9. Validate
|
||||
|
||||
After creating the file:
|
||||
|
||||
1. Check that all required schema fields are present
|
||||
2. Verify the file path is correct
|
||||
3. Ensure frontmatter is valid YAML
|
||||
4. Confirm dates are in correct format
|
||||
|
||||
### 10. Next Steps
|
||||
|
||||
Inform the user:
|
||||
- File location
|
||||
- How to add images (if applicable)
|
||||
- How to preview: `npm run dev`
|
||||
- How to build: `npm run build`
|
||||
|
||||
## Example Output
|
||||
|
||||
**For a collection-based blog:**
|
||||
|
||||
File: `src/content/blog/getting-started-with-astro.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: 'Getting Started with Astro'
|
||||
description: 'Learn the basics of building fast websites with Astro'
|
||||
pubDate: 2024-01-15
|
||||
author: 'John Doe'
|
||||
tags: ['astro', 'tutorial', 'getting-started']
|
||||
draft: false
|
||||
---
|
||||
|
||||
# Introduction
|
||||
|
||||
Welcome to this comprehensive guide on getting started with Astro!
|
||||
|
||||
## What is Astro?
|
||||
|
||||
Astro is a modern web framework...
|
||||
|
||||
## Setting Up Your First Project
|
||||
|
||||
Let's walk through the setup process...
|
||||
|
||||
## Conclusion
|
||||
|
||||
You've learned the basics of Astro. Happy building!
|
||||
```
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] User requirements gathered (title, description, etc.)
|
||||
- [ ] Collection schema reviewed and understood
|
||||
- [ ] Slug generated from title
|
||||
- [ ] Correct file location determined
|
||||
- [ ] Frontmatter created matching schema
|
||||
- [ ] All required fields included
|
||||
- [ ] Dates in ISO format (YYYY-MM-DD)
|
||||
- [ ] Starter content added
|
||||
- [ ] File created successfully
|
||||
- [ ] Validation passed (schema compliance)
|
||||
- [ ] User informed of next steps
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Always check the existing collection schema before creating frontmatter
|
||||
- Use ISO date format: `2024-01-15` or `2024-01-15T10:00:00Z`
|
||||
- Ensure tags are arrays: `['tag1', 'tag2']` not `'tag1, tag2'`
|
||||
- Use proper YAML syntax (quoted strings, correct indentation)
|
||||
- Don't include `layout` field for content collection posts
|
||||
- Do include `layout` field for page-based posts
|
||||
|
||||
## Error Prevention
|
||||
|
||||
- Verify collection name exists in config
|
||||
- Check all required fields are present
|
||||
- Validate date format
|
||||
- Ensure proper YAML syntax
|
||||
- Don't mix collection and page patterns
|
||||
- Test with `npm run dev` after creation
|
||||
Reference in New Issue
Block a user