Course Builder Development Guide
Course Builder is a real-time multiplayer CMS (Content Management System) designed specifically for building and deploying developer education products. This monorepo contains multiple applications and shared packages that work together to provide a comprehensive platform for creating, managing, and delivering educational content.
Overview
Course Builder Development Guide
Project Overview
Course Builder is a real-time multiplayer CMS (Content Management System) designed specifically for building and deploying developer education products. This monorepo contains multiple applications and shared packages that work together to provide a comprehensive platform for creating, managing, and delivering educational content.
Main Features
- Content management for courses, modules, and lessons
- Video processing pipeline with transcription
- AI-assisted content creation and enhancement
- Real-time collaboration for content creators
- Authentication and authorization
- Payment processing and subscription management
- Progress tracking for students
Key Technologies
- Framework: Next.js (App Router)
- Language: TypeScript
- Monorepo: Turborepo with PNPM workspaces
- Database: Drizzle ORM with MySQL/PlanetScale
- Authentication: NextAuth.js
- Styling: Tailwind CSS
- API: tRPC for type-safe API calls
- Real-time: PartyKit/websockets for collaboration
- Event Processing: Inngest for workflows and background jobs
- Media: Mux for video processing, Deepgram for transcription
- AI: OpenAI/GPT for content assistance
- Payments: Stripe integration
Repository Structure
Apps (/apps)
ai-hero: Main application focused on AI-assisted learningastro-party: An Astro-based implementationcourse-builder-web: The main Course Builder web applicationegghead: Integration with egghead.io platformepic-react: Specific implementation for React coursesgo-local-first: Implementation with local-first capabilities
Packages (/packages)
-
Core Functionality:
core: Framework-agnostic core libraryui: Shared UI components based on Radix/shadcnadapter-drizzle: Database adapter for Drizzle ORMnext: Next.js specific bindingscommerce-next: Commerce components and functionality
-
Utility Packages:
utils-ai: AI-related utilitiesutils-auth: Authentication and authorization utilitiesutils-aws: AWS service utilitiesutils-browser: Browser-specific utilities (cookies, etc.)utils-core: Core utilities likeguidutils-email: Email-related utilitiesutils-file: File handling utilitiesutils-media: Media processing utilitiesutils-resource: Resource filtering and processing utilitiesutils-search: Search functionality utilitiesutils-seo: SEO utilitiesutils-string: String manipulation utilitiesutils-ui: UI utilities likecn
Other Directories
cli: Command-line tools for project bootstrappingdocs: Documentation including shared utilities guideinstructions: Detailed instructions for development tasksplop-templates: Templates for code generation
Command Reference
Build Commands
pnpm build:all- Build all packages and appspnpm build- Build all packages (not apps)pnpm build --filter="ai-hero"- Build specific apppnpm dev:all- Run dev environment for all packages/appspnpm dev- Run dev environment for packages only
Testing
pnpm test- Run all testspnpm test --filter="@coursebuilder/utils-file"- Test specific packagepnpm test:watch- Run tests in watch modecd packages/package-name && pnpm test- Run tests for specific packagecd packages/package-name && pnpm test src/path/to/test.test.ts- Run a single test filecd packages/package-name && pnpm test:watch src/path/to/test.test.ts- Watch single test file
Linting and Formatting
pnpm lint- Run linting on all packages/appspnpm format:check- Check formatting without changing filespnpm format- Format all files using Prettierpnpm typecheck- Run TypeScript type checkingpnpm manypkg fix- Fix dependency version mismatches and sort package.json files
Use --filter="APP_NAME" to run commands for a specific app
Code Generation and Scaffolding
Creating New Utility Packages
Use the custom Plop template to create new utility packages:
# Create a new utility package using the template
pnpm plop package-utils <domain> <utilityName> <functionName> "<utilityDescription>"
# Example:
pnpm plop package-utils browser cookies getCookies "Browser cookie utility"
# With named parameters:
pnpm plop package-utils -- --domain browser --utilityName cookies --functionName getCookies --utilityDescription "Browser cookie utility"
This will create a properly structured package with:
- Correct package.json with exports configuration
- TypeScript configuration
- Basic implementation with proper TSDoc comments
- Test scaffolding
Working with Utility Packages
Adding Dependencies
When updating package.json files to add dependencies:
- Use string replacement with Edit tool to add dependencies
- Maintain alphabetical order of dependencies
- Don't replace entire sections, just add the new line
Example of proper package.json edit:
"@coursebuilder/utils-media": "1.0.0",
"@coursebuilder/utils-seo": "1.0.0",
// Replace with:
"@coursebuilder/utils-media": "1.0.0",
"@coursebuilder/utils-resource": "1.0.0", // New line added here
"@coursebuilder/utils-seo": "1.0.0",
Framework Compatibility
When creating utility packages that interact with framework-specific libraries:
- Keep framework-specific dependencies (React, Next.js, etc.) as peer dependencies
- For utilities that use third-party libraries (like Typesense, OpenAI), provide adapters rather than direct implementations
- Be careful with libraries that might conflict with framework internals
- Test builds across multiple apps to ensure compatibility
Code Style
- Formatting: Single quotes, no semicolons, tabs (width: 2), 80 char line limit
- Imports: Organized by specific order (React → Next → 3rd party → internal)
- File structure: Monorepo with apps in /apps and packages in /packages
- Package Manager: PNPM (v8.15.5+)
- Testing Framework: Vitest
Conventional Commits
We use conventional commits with package/app-specific scopes:
- Format:
<type>(<scope>): <description> - Types:
feat,fix,refactor,style,docs,test,chore - Scopes:
- App codes:
aih(ai-hero),egh(egghead),eweb(epic-web) - Packages:
utils-email,core,ui,mdx-mermaid, etc.
- App codes:
Examples:
fix(egh): convert SanityReference to SanityArrayElementReferencestyle(mdx-mermaid): make flowcharts nicerrefactor(utils): implement SEO utility package with re-export patternfeat(utils-email): create email utilities package with sendAnEmail
Common Patterns
Dependency Management
When adding dependencies to packages in the monorepo, ensure that:
- All packages use consistent dependency versions
- Dependencies in package.json files are sorted alphabetically
If you encounter linting errors related to dependency versions or sorting:
# Fix dependency version mismatches and sort package.json files
pnpm manypkg fix
Re-export Pattern for Backward Compatibility
When creating shared utility packages, use the re-export pattern to maintain backward compatibility:
// In /apps/app-name/src/utils/some-utility.ts
// Re-export from the shared package
This preserves existing import paths throughout the codebase while moving the implementation to a shared package.
Important: Avoid Object.defineProperty for Re-exports
Do NOT use Object.defineProperty(exports, ...) for re-exports as this can cause conflicts with framework internals, especially with Next.js and tRPC:
// DON'T DO THIS - can cause "Cannot redefine property" errors in build
Object.defineProperty(exports, 'someFunction', {
value: function() { /* implementation */ }
})
// INSTEAD DO THIS - standard export pattern
These conflicts typically manifest as "Cannot redefine property" errors during build and are difficult to debug. They occur because the build process may try to define the same property multiple times through different bundling mechanisms.
TSDoc Comments for Utilities
Always include comprehensive TSDoc comments for utility functions:
/**
* Brief description of what the function does
*
* Additional details if needed
*
* @param paramName - Description of the parameter
* @returns Description of the return value
*
* @example
* ```ts
* // Example usage code
* const result = myFunction('input')
* ```
*/
App Directory Structure Pattern
Most apps follow this general directory structure:
src/app- Next.js App Router pages and layoutssrc/components- React componentssrc/lib- Domain-specific business logicsrc/utils- Utility functionssrc/db- Database schema and queriessrc/server- Server-side functions and API routessrc/hooks- React hookssrc/trpc- tRPC router and procedures
Database Schema
Most applications use Drizzle ORM with a schema in src/db/schema.ts that typically includes:
- Users and authentication
- Content resources (courses, modules, lessons)
- Progress tracking
- Purchases and subscriptions
Auth Pattern
Authentication usually follows this pattern:
- NextAuth.js for authentication providers
- CASL ability definitions for authorization
- Custom middleware for route protection