Development
Learn how to contribute to shadcnai, set up the development environment, and understand the project architecture.
Project Structure
packages/shadcnai/
├── src/
│ ├── cli.ts # CLI entry point
│ ├── commands/
│ │ ├── index.ts # Command exports
│ │ └── theme.ts # Theme generation command
│ ├── constants/
│ │ └── models.ts # AI model definitions
│ ├── lib/
│ │ ├── animations.ts # CLI animations and UI
│ │ ├── css.ts # CSS generation utilities
│ │ ├── env.ts # Environment validation
│ │ ├── files.ts # File operations
│ │ ├── models.ts # AI model configurations
│ │ └── utils.ts # General utilities
│ ├── prompts/
│ │ └── theme-generation.ts # AI prompts for theme generation
│ ├── services/
│ │ └── theme-generator.ts # Core theme generation service
│ └── types/
│ └── theme.ts # TypeScript type definitions
├── package.json
├── tsconfig.json
└── README.md
Development Setup
Prerequisites
- Node.js ≥ 18.0.0
- npm ≥ 9.0.0
- Git for version control
- AI provider API keys for testing
Getting Started
-
Clone the repository:
git clone https://github.com/vivek9patel/shadcnai.git cd shadcnai/packages/shadcnai
-
Install dependencies:
npm install
-
Set up environment variables:
# Copy example environment file cp .env.example .env # Add your API keys echo "GOOGLE_GENERATIVE_AI_API_KEY=your-key" >> .env echo "OPENAI_API_KEY=your-key" >> .env # ... add other providers as needed
-
Build the project:
npm run build
-
Run in development mode:
npm run dev
-
Test your changes:
# Test the CLI locally node dist/cli.js theme "test theme" # Or link for global testing npm link shadcnai theme "test theme"
Development Scripts
Available Commands
# Clean build directory
npm run clean
# Build TypeScript to JavaScript
npm run build
# Run tests
npm run test
# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
# Type check without building
npm run typecheck
# Watch mode for development
npm run dev
# Prepare for publishing
npm run prepack
Development Workflow
-
Start development mode:
npm run dev
-
Make your changes in the
src/
directory -
Test changes locally:
node dist/cli.js theme "your test description"
-
Run tests:
npm test
-
Lint and fix:
npm run lint:fix
Architecture Overview
Core Components
CLI Interface (cli.ts
)
- Main entry point using yargs
- Command parsing and validation
- Help and version handling
Command System (commands/
)
- Modular command structure
- Theme generation command implementation
- Parameter validation and processing
AI Integration (services/theme-generator.ts
)
- Abstracted AI provider interactions
- Model selection and switching
- Error handling and retries
File Management (lib/files.ts
)
- Theme file generation
- Auto-import with shadcn CLI
- File system operations
Type System (types/theme.ts
)
- Zod schemas for validation
- TypeScript type definitions
- Data structure consistency
Data Flow
graph TD
A[CLI Input] --> B[Command Parser]
B --> C[Parameter Validation]
C --> D[Environment Check]
D --> E[AI Service]
E --> F[Theme Generation]
F --> G[File Operations]
G --> H[Auto-Import]
H --> I[Output]
Adding New Features
Adding a New AI Provider
-
Update model constants (
constants/models.ts
):export const AI_PROVIDERS = { // ... existing providers newProvider: { name: "New Provider", envKey: "NEW_PROVIDER_API_KEY", models: ["model-1", "model-2"] as const, }, } as const;
-
Add provider integration (
lib/models.ts
):import { createNewProvider } from "@ai-sdk/new-provider"; export function createModel(model: SupportedModel) { // ... existing provider logic if (provider?.key === "newProvider") { const newProvider = createNewProvider({ apiKey: process.env.NEW_PROVIDER_API_KEY, }); return newProvider(model); } }
-
Update environment validation (
lib/env.ts
):export async function validateEnvironment(model: SupportedModel) { // ... existing validation if (provider?.envKey === "NEW_PROVIDER_API_KEY") { if (!process.env.NEW_PROVIDER_API_KEY) { throw new Error(`Missing ${provider.envKey} environment variable`); } } }
Adding New Command Options
-
Update command builder (
commands/theme.ts
):builder: { // ... existing options newOption: { alias: "n", describe: "Description of new option", type: "string" as const, default: "default-value", }, }
-
Handle option in command (
commands/theme.ts
):handler: async (argv: any) => { const { newOption } = argv; // Use the new option in your logic const result = await ThemeGeneratorService.generateTheme(description, { newOption, }); };
Extending Theme Schema
-
Update Zod schema (
types/theme.ts
):export const ThemeSchema = z.object({ // ... existing fields newField: z.string().describe("Description of new field"), });
-
Update generation logic (
services/theme-generator.ts
):// Ensure AI prompt includes new field // Update file generation to handle new field
Testing
Running Tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
Writing Tests
Test files are located alongside source files with .test.ts
extension:
// src/lib/utils.test.ts
import { describe, it, expect } from "@jest/globals";
import { yourFunction } from "./utils";
describe("yourFunction", () => {
it("should handle valid input", () => {
const result = yourFunction("valid input");
expect(result).toBe("expected output");
});
it("should throw on invalid input", () => {
expect(() => yourFunction("invalid")).toThrow();
});
});
Testing with AI Models
For testing with actual AI models, use environment variables:
// Only run AI tests if API keys are available
const hasApiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY;
describe("ThemeGenerator", () => {
it.skipIf(!hasApiKey)("should generate valid theme", async () => {
const result = await generateTheme("test theme");
expect(result).toMatchSchema(ThemeSchema);
});
});
Code Style and Standards
TypeScript Configuration
The project uses strict TypeScript settings:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
ESLint Configuration
Code style is enforced with ESLint:
# Check linting
npm run lint
# Fix auto-fixable issues
npm run lint:fix
Formatting Standards
- Use 2 spaces for indentation
- Prefer
const
overlet
- Use template literals for string interpolation
- Add JSDoc comments for public APIs
- Use descriptive variable names
Commit Messages
Follow conventional commits:
feat: add support for new AI provider
fix: handle API rate limiting gracefully
docs: update installation instructions
test: add tests for theme validation
chore: update dependencies
Contributing
Contribution Process
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name
- Make your changes
- Add tests for new functionality
- Update documentation if needed
- Run tests and linting:
npm test npm run lint
- Commit with conventional format
- Push and create pull request
Pull Request Guidelines
- Include clear description of changes
- Add tests for new features
- Update documentation
- Ensure CI passes
- Link related issues
Issue Reporting
When reporting issues:
- Use issue templates
- Include reproduction steps
- Provide environment details
- Include error messages
- Add relevant logs
Release Process
Versioning
The project follows semantic versioning:
- Patch (1.0.1): Bug fixes
- Minor (1.1.0): New features (backward compatible)
- Major (2.0.0): Breaking changes
Publishing
Releases are automated through GitHub Actions:
- Create release PR with version bump
- Merge to main triggers CI/CD
- Automated testing and building
- Publish to npm if tests pass
- Create GitHub release with changelog
Manual Release
For manual releases:
# Update version
npm version patch|minor|major
# Build and test
npm run build
npm test
# Publish
npm publish
Debugging
Common Issues
Build Errors
# Clean and rebuild
npm run clean
npm run build
API Errors
# Check environment variables
echo $GOOGLE_GENERATIVE_AI_API_KEY
# Test with verbose logging
DEBUG=shadcnai:* shadcnai theme "test"
Import Errors
# Verify shadcn CLI is available
npx shadcn@latest --help
# Check project structure
ls components.json
Debug Mode
Enable debug logging:
export DEBUG=shadcnai:*
shadcnai theme "debug theme"
Performance Optimization
AI Model Performance
- Model Selection: Choose appropriate models for use case
- Prompt Optimization: Craft efficient prompts
- Caching: Cache common results
- Retry Logic: Handle transient failures
File Operations
- Async Operations: Use async/await for I/O
- Error Handling: Graceful failure handling
- Memory Management: Stream large files
Documentation
Code Documentation
Use JSDoc for public APIs:
/**
* Generates a theme based on the provided description
* @param description - Human-readable theme description
* @param options - Generation options
* @returns Promise resolving to generated theme
*/
export async function generateTheme(
description: string,
options: GenerationOptions
): Promise<Theme> {
// Implementation
}
Updating Documentation
Documentation is built with Fumadocs:
- Edit MDX files in
app/docs/
- Test locally:
npm run dev
- Build:
npm run build
- Deploy: Automatic on merge to main
Getting Help
Community Resources
- GitHub Issues: Bug reports and feature requests
- Discussions: General questions and ideas
- Discord: Real-time community chat (if available)
Development Questions
For development-specific questions:
- Check existing issues and discussions
- Review the codebase and tests
- Ask in GitHub discussions
- Create detailed issue if needed
Next Steps
Ready to contribute? Start by:
- Setting up the development environment
- Exploring the codebase
- Running tests to understand the system
- Finding a good first issue to work on
- Joining the community discussions
Thank you for contributing to shadcnai! 🎨