A CLI tool for running raffles via the terminal with CSV/TXT input, column parsing, and animated effects.
# Install dependencies
pnpm install
# Build the project
pnpm run build
# Optionally link for global usage
npm link# Show help
raffle --help
# Show version
raffle --version
raffle version
# Run a raffle
raffle run <file> [options]# Select 1 winner from a specific column
raffle run "C:\path\to\participants.csv" --header --column name
# Select multiple winners
raffle run "C:\path\to\participants.csv" --header --column name --winners 3
# Specify custom delimiter
raffle run "C:\path\to\participants.csv" --header --column name --delimiter ";"# Select 1 winner from a list of names
raffle run "C:\path\to\names.txt"
# Select multiple winners
raffle run "C:\path\to\names.txt" --winners 5<file>- Input file path (CSV or TXT) - supports both absolute and relative paths-c, --column <name>- Column name for participant names (CSV with headers)-h, --header- File has header row (default: false)-d, --delimiter <char>- Delimiter character for CSV (auto-detected if not specified)-w, --winners <count>- Number of winners to select (default: 1)--show-index- Show row indices in output (default: false)--countdown <seconds>- Countdown duration in seconds (default: "3")--no-animation- Disable animated effects
This repository includes sample test files for trying out the raffle functionality:
Contains employee data with columns: name, email, department, years_experience
# Test CSV with header and specific column
raffle run "src/test/list.csv" --header --column name --winners 2
# Test CSV selecting from different column
raffle run "src/test/list.csv" --header --column department --winners 1
# Test CSV with multiple winners and index display
raffle run "src/test/list.csv" --header --column name --winners 3 --show-indexContains a simple list of names, one per line
# Test simple text file
raffle run "src/test/list.txt" --winners 1
# Test multiple winners from text file
raffle run "src/test/list.txt" --winners 4raffle run "C:\data\participants.csv" --header --column "Full Name" --winners 1Output:
Reading file: C:\data\participants.csv
Processing participants...
Found 50 participants
Detected delimiter: ","
π Selected 1 winner(s):
1. John Smith
raffle run "C:\data\names.txt" --winners 3Output:
Reading file: C:\data\names.txt
Processing participants...
Found 20 participants
Detected delimiter: ","
π Selected 3 winner(s):
1. Alice Johnson
2. Bob Wilson
3. Carol Davis
raffle run "C:\data\employees.csv" --header --column department --winners 2 --show-indexOutput:
Reading file: C:\data\employees.csv
Processing participants...
Found 100 participants
Detected delimiter: ","
π Selected 2 winner(s):
1. [Index] Engineering
2. [Index] Marketing
- Supports any delimiter (comma, semicolon, tab, pipe, etc.)
- Auto-detects delimiter if not specified
- Can have header row with column names
- Can select specific columns for participant extraction
- One participant per line
- Simple plain text format
- No delimiter needed
- Supports both absolute and relative file paths
- Relative paths are resolved from current working directory
- Examples:
- β
"C:\Users\YourName\Desktop\participants.csv"(absolute) - β
"/home/user/data/names.txt"(absolute) - β
"./participants.csv"(relative) - β
"data/names.txt"(relative) - β
"src/test/list.csv"(relative)
- β
- Path traversal protection (no
..or~allowed) - Only reads files, never writes or modifies
- Validates file existence before processing
src/
βββ cli.ts # CLI entrypoint - handles I/O and wires commands
βββ commands/ # Thin command layer - pure functions
β βββ index.ts # Command factory exports
β βββ runRaffle.ts # Main raffle command logic
βββ utils/ # Pure utility functions - no side effects
β βββ detectDelimiter.ts # CSV delimiter detection
β βββ parseFile.ts # File content parsing
β βββ format.ts # Display formatting
β βββ rng.ts # Random number generation
β βββ pickWinners.ts # Winner selection logic
β βββ io.ts # I/O helpers (only place allowed to touch fs)
βββ test/ # Test data files
βββ list.csv # Sample CSV with employee data
βββ list.txt # Sample text file with names
This project follows functional programming principles:
- No classes - Everything is implemented as pure functions
- Dependency injection - Commands receive utilities as parameters
- Side effects isolation - Only
cli.tsandutils/io.tsperform I/O - Pure functions - All utilities are deterministic and testable
- Immutable data - Functions don't modify input parameters
# Development
pnpm run dev # Watch mode for development
pnpm run build # Compile TypeScript to JavaScript
pnpm run start # Run the compiled CLI
# Code Quality
pnpm run lint # Run ESLint on source files
pnpm run lint:fix # Run ESLint with auto-fix
pnpm run format # Format code with Prettier
pnpm run format:check # Check code formatting
# Git & Release
pnpm run commit # Interactive commit with Commitizen
pnpm run release # Create a new release
pnpm run prepare # Setup Husky hooks- Follow the functional programming style
- Add JSDoc documentation to all functions
- Write tests for new utilities
- Use conventional commits
- Run linting and formatting before committing
ISC