Thank you for your interest in contributing. This document covers everything you need to get started — from local setup through the pull request review process.
- Code of Conduct
- Getting Started
- Repository Structure
- Development Setup
- Coding Standards
- Testing Requirements
- Linting and Formatting
- Commit Standards
- Branching Strategy
- Pull Request Process
- Security Practices
- Performance Expectations
- Review Workflow
- First-Time Contributors
This project follows the Contributor Covenant Code of Conduct. By participating, you agree to uphold these standards. Report unacceptable behavior to ofcskn1@gmail.com.
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/<your-username>/minimalblock.git - Add the upstream remote:
git remote add upstream https://github.com/ofcskn/minimalblock.git
- Complete Development Setup
- Create a branch, make your changes, and open a pull request
minimalblock/
├── apps/
│ ├── web/ # React 19 + Vite SPA
│ ├── api/ # Cloudflare Worker API
│ └── docs/ # VitePress documentation site
├── libs/
│ ├── core/ # Domain entities, value objects, ports (no framework deps)
│ ├── data/ # Supabase repository implementations
│ ├── ai/ # Gemini clients, generators, prompts
│ ├── features/ # React hooks connecting UI to data layer
│ ├── ui/ # Shared React component library
│ └── trendyol/ # Trendyol marketplace integration
├── supabase/
│ └── migrations/ # Sequential SQL migrations
└── docs/ # Static documentation (Diátaxis framework)
Dependency rules (enforced by Nx):
apps/*may import fromlibs/*libs/featuresandlibs/uimay import fromlibs/coreandlibs/datalibs/aimay import fromlibs/corelibs/corehas no workspace dependencies (fully portable)- No circular dependencies
- Node.js 20.x
- pnpm 10.30.3 (
npm install -g pnpm@10.30.3) - Supabase account and project
- Google Gemini API key
# Install dependencies
pnpm install
# Copy and configure environment files
cp .env.example .env
cp apps/api/.env.example apps/api/.env
# Edit both files with your Supabase and Gemini credentials
# Apply database migrations
supabase link --project-ref <project-ref>
supabase db push
# Start development servers
pnpm nx run-many -t serve -p web apiWeb app runs at http://localhost:4200, API at http://localhost:8787.
- Strict mode is enabled (
tsconfig.base.json). Noany, noas any. - Prefer
interfaceovertypefor object shapes that will be extended or implemented. - Use
typefor unions, intersections, and computed types. - All public functions must have explicit return types.
- Prefer
unknownoveranywhen the type is genuinely unknown. - Prefer named exports over default exports.
- Use
readonlyon arrays and objects that should not be mutated. - Use optional chaining (
?.) and nullish coalescing (??) instead of||for nullable values.
// Good
interface ProductRepository {
findById(id: string): Promise<Product | null>;
}
// Bad — implicit any, missing return type
function getProduct(id) {
return fetch(`/products/${id}`).then(r => r.json());
}- Functional components only — no class components.
- Props interfaces are named
<ComponentName>Props. - Co-locate related logic (hooks, utils) with the component that primarily uses them.
- Extract multi-line JSX into named sub-components rather than inline arrow functions in render.
- Avoid
useEffectfor derived state — preferuseMemoor computing directly. - Use React Query for all server state; React Context for app-wide UI state only.
- Components in
libs/uimust be stateless or use only local state — no direct Supabase calls.
// Good
interface ProductCardProps {
product: Product;
onSelect: (id: string) => void;
}
export function ProductCard({ product, onSelect }: ProductCardProps) { ... }
// Bad — missing prop types, default export
export default function ProductCard({ product, onSelect }) { ... }| Content | Convention | Example |
|---|---|---|
| React component | PascalCase.tsx |
ProductCard.tsx |
| Hook | use-kebab-case.ts |
use-gallery.ts |
| Service/class | kebab-case.ts |
gemini-client.ts |
| Utility | kebab-case.ts |
file-validation.ts |
| Test | *.spec.ts / *.spec.tsx |
ProductCard.spec.tsx |
| Prompt | kebab-case.prompt.ts |
material-inference.prompt.ts |
Enforced by ESLint. Order:
- Node built-ins
- Third-party packages
- Workspace packages (
@minimalblock/*) - Relative imports (closest first)
- All pure functions and utilities in
libs/core - All repository methods in
libs/data(use a mock Supabase client) - All Gemini client methods in
libs/ai(useMockImageAnalyzer) - All React hooks in
libs/features(use@testing-library/react) - Critical UI components in
libs/ui
- Wiring components that only compose library components with no logic
- One-line utility wrappers
- Configuration files
# All tests
pnpm nx run-many -t test
# Specific library
pnpm nx test core
# Watch mode
pnpm nx test web --watch
# With coverage
pnpm nx test ai --coverage- Use
describe/itblocks with clear human-readable descriptions. - One assertion per test where possible.
- Use
libs/ai/src/lib/mock/mock-image-analyzer.tsfor AI tests — never call the real Gemini API in tests. - Do not add
@ts-ignoreoras anyto make tests pass. - Test file lives adjacent to the module it tests.
# Lint (ESLint 9 flat config)
pnpm nx run-many -t lint
# Auto-fix lint issues
pnpm nx run-many -t lint --fix
# Check formatting (Prettier 3)
pnpm nx format:check
# Auto-fix formatting
pnpm nx format:writeCI will reject PRs with lint errors or formatting violations. Run these before pushing.
We follow the Conventional Commits specification.
<type>(<scope>): <short description>
[optional body]
[optional footer(s)]
| Type | When to use |
|---|---|
feat |
A new feature visible to users or developers |
fix |
A bug fix |
perf |
Performance improvement with no behavior change |
refactor |
Code change that neither adds a feature nor fixes a bug |
test |
Adding or updating tests |
docs |
Documentation changes only |
chore |
Build system, CI, dependency updates |
style |
Formatting, whitespace (no logic change) |
Use the library or app name: web, api, docs, core, data, ai, ui, features, trendyol.
feat(ai): add return-risk scoring to Gemini image analyzer
fix(data): correct RLS policy on generation_feedback table
perf(web): virtualize gallery grid with TanStack Virtual
docs(api): document /convert endpoint request schema
chore(ci): upgrade Nx Cloud agents to linux-medium-js
- Subject line: max 72 characters, lowercase, no trailing period
- Body: explain why, not what (the diff shows what)
- Breaking changes: add
BREAKING CHANGE:footer with migration instructions
| Branch | Purpose |
|---|---|
main |
Production-ready code. CI must pass. |
development |
Integration branch for in-progress work |
feat/<name> |
New features |
fix/<name> |
Bug fixes |
perf/<name> |
Performance improvements |
docs/<name> |
Documentation-only changes |
chore/<name> |
Maintenance, dependency updates |
- Branch from
developmentfor all feature work. - Keep branches short-lived (merge within a sprint if possible).
- Rebase onto
developmentbefore opening a PR — no merge commits in feature branches. - Delete the branch after it is merged.
# Start a feature
git fetch upstream
git checkout -b feat/hotspot-animation upstream/development
# Keep up to date
git fetch upstream
git rebase upstream/development
# Push
git push -u origin feat/hotspot-animation- Open a draft PR as soon as you push the first commit — allows early feedback.
- Fill out the pull request template completely.
- Ensure CI passes: lint, typecheck, tests, build.
- Request review from a maintainer once the PR is ready.
- Respond to all review comments within 3 business days.
- Squash-merge after approval.
Before marking a PR ready for review:
-
pnpm nx run-many -t lintpasses -
pnpm nx run-many -t typecheckpasses -
pnpm nx run-many -t testpasses (or the affected subset) - New code is covered by tests
- Docs updated if behavior changes
- No secrets, credentials, or
.envfiles committed - Migrations are sequential and named correctly (e.g.,
017_...sql)
- Never commit secrets, API keys,
.envfiles, or credentials. - Use
VITE_prefix only for variables safe to expose in the browser. GEMINI_API_KEYandSUPABASE_SERVICE_ROLE_KEYmust stay inapps/apisecrets only.- All new Supabase tables must have RLS enabled with explicit owner-scoped policies.
- Sanitize any user-supplied input before passing it to AI prompts (prompt injection risk).
- Use parameterized queries — never string-concatenate user input into SQL.
- Validate file types server-side before processing uploads — MIME type checking only is insufficient.
Report security vulnerabilities privately — see .github/SECURITY.md.
When adding new features, consider:
- Bundle size: New npm dependencies need justification — check the Vite bundle analyzer before adding.
- Supabase queries: Add indexes for any query that filters on non-indexed columns.
- AI calls: Gemini calls are expensive. Cache results where possible; don't call in loops.
- React renders: Profile with React DevTools before submitting components that render frequently.
- Worker cold starts: Keep
apps/apibundle small — avoid heavy dependencies.
Run the bundle analyzer:
pnpm nx build web -- --sourcemap- Keep PRs focused — one logical change per PR.
- Write a clear PR description explaining why the change is needed.
- Annotate non-obvious decisions with code comments in the PR description, not inline comments.
- Review for correctness, security, performance, and maintainability — in that order.
- Suggest alternatives rather than just identifying problems.
- Approve only when all blocking comments are resolved.
- Use GitHub's "Request changes" only for blocking issues; use "Comment" for suggestions.
Not sure where to start? Look for issues tagged good first issue on the GitHub issue tracker.
For questions that don't fit an issue, open a GitHub Discussion or see SUPPORT.md.
We appreciate every contribution — including documentation fixes, typo corrections, and translation improvements.