Estructura del repositorio
cultiva-ia/monorepo
├── apps/
├── web/Next.js 14
│ ├── app/
│ └── package.json
├── dashboard/Next.js 14
│ ├── app/
│ └── package.json
└── docs/Astro
├── packages/
├── ui/@cultiva/ui
├── ai-toolkit/@cultiva/ai-toolkit
├── analytics/@cultiva/analytics
├── tsconfig/@cultiva/tsconfig
└── eslint-config/@cultiva/eslint-config
├── turbo.json
├── package.json
├── pnpm-workspace.yaml
├── .changeset/
└── .github/workflows/
Paquetes del workspace
App
apps/web
Web marketing principal. SSR + ISR, blog de contenido, landing pages de servicios.
Next.js 14
App Router
Tailwind
App
apps/dashboard
Panel de clientes. Reporting de campañas, métricas IA, gestión de proyectos activos.
Next.js 14
Recharts
tRPC
App
apps/docs
Documentación técnica del arsenal de 2.000+ skills. Búsqueda full-text, categorías.
Astro 4
MDX
Pagefind
Package
@cultiva/ui
Librería de componentes compartidos. Button, Card, Badge, Modal, DataTable, Charts.
React 18
tsup
Storybook
Package
@cultiva/ai-toolkit
Wrappers sobre Claude API. Streaming, tool use, agentes multi-step, rate limiting.
Anthropic SDK
Zod
TypeScript
Package
@cultiva/analytics
Utilidades de tracking: eventos GA4, Plausible, atribución de campañas, GTM helpers.
GA4
Plausible
TypeScript
Package
@cultiva/tsconfig
Presets TypeScript: base, next, astro, library. Strict mode habilitado en todos.
TypeScript 5
strict
Package
@cultiva/eslint-config
Configuración ESLint base con reglas Next.js, React Hooks, import ordering y accesibilidad.
ESLint 9
Flat Config
Configuraciones clave
{
"$schema": "https://turbo.build/schema.json",
"remoteCache": {
"enabled": true // cache remota en Vercel
},
"pipeline": {
"build": {
"dependsOn": ["^build"], // deps primero
"outputs": [
"dist/**",
".next/**",
"!.next/cache/**"
]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": [] // sin output, solo status
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
},
"dev": {
"cache": false, // dev nunca se cachea
"persistent": true // proceso continuo
}
}
}
packages:
- "apps/*"
- "packages/*"
{
"name": "cultiva-ia",
"private": true,
"packageManager": "pnpm@9.4.0",
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"lint": "turbo run lint",
"type-check": "turbo run type-check",
"release": "changeset publish"
},
"devDependencies": {
"turbo": "^1.13.0",
"@changesets/cli": "^2.27.0",
"typescript": "^5.4.0"
}
}
{
"name": "@cultiva/ui",
"version": "1.2.0",
"private": false, // publicado en npm privado
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" },
"./button": { "import": "./dist/button.js", "types": "./dist/button.d.ts" },
"./data-table": { "import": "./dist/data-table.js", "types": "./dist/data-table.d.ts" }
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts",
"dev": "tsup src/index.ts --format esm,cjs --dts --watch"
},
"devDependencies": {
"@cultiva/tsconfig": "workspace:*",
"tsup": "^8.0.0",
"typescript": "^5.4.0"
},
"peerDependencies": { "react": "^18.0.0" }
}
Pipeline de tareas Turborepo
| Tarea |
Depende de |
Cache |
Outputs |
Modo |
| build |
^build (deps primero) |
SI |
dist/** .next/** |
Paralelo por paquete |
| test |
build (mismo paquete) |
SI |
coverage/** |
Paralelo |
| lint |
— (ninguna) |
SI |
— (solo status) |
Paralelo |
| type-check |
^build (deps primero) |
SI |
— (solo status) |
Paralelo |
| dev |
— (ninguna) |
NO |
— (proceso vivo) |
PERSISTENT |
Grafo de dependencias de build
📦
@cultiva/tsconfig
Sin deps
Capa 0
→
🎨
@cultiva/ui
← tsconfig
Capa 1
→
🤖
@cultiva/ai-toolkit
← tsconfig
Capa 1
→
📊
@cultiva/analytics
← tsconfig
Capa 1
→
🌐
apps/web
← ui, analytics
Capa 2
→
🏠
apps/dashboard
← ui, ai-toolkit, analytics
Capa 2
→
CI/CD — GitHub Actions
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Build + Test (Turbo)
run: pnpm turbo run build test lint type-check
env:
# Cache remota en Vercel
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: cultiva-ia
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v3
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run build
- name: Create Release PR or Publish
uses: changesets/action@v1
with:
publish: pnpm release
title: "chore: release packages"
commit: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
1
pnpm install --frozen-lockfile
Instala dependencias exactas del lockfile. Node modules cacheados por pnpm action.
2
turbo run build (cache remota)
Builds cacheados en Vercel Remote Cache. Solo reconstruye paquetes afectados por el PR.
3
turbo run test lint type-check
Paralelo. Sólo ejecuta los paquetes que cambiaron vs. caché anterior.
4
changesets/action → PR de release
Crea un PR "Version Packages" automático o publica en npm si el PR se mergea.
Flujo de publicación con Changesets
1. Developer
pnpm changeset
→
2. Commit .changeset
feat: new Button variant
→
3. PR → main
changeset/action crea PR
→
4. Merge release PR
bump @cultiva/ui 1.2.0→1.3.0
→
5. npm publish
@cultiva/ui@1.3.0 ✓
Errores frecuentes a evitar
🔄
Dependencias circulares
@cultiva/ui no puede importar @cultiva/ai-toolkit si este importa @cultiva/ui. Usar madge para detectarlas.
👻
Phantom dependencies
Usar una librería instalada en la raíz sin declararla en el package.json del paquete. Siempre declarar explícitamente.
💾
Cache inputs mal configurados
Si .env no está en globalDependencies, cambiar secrets no invalida el cache. Resultado: builds con valores obsoletos.
🔗
workspace:* en producción
Las referencias workspace:* deben reemplazarse por versiones reales antes de publicar. Changesets lo hace automáticamente.
⚡
dev sin --filter en repos grandes
pnpm dev arranca todas las apps. Usar turbo run dev --filter=web para levantar solo lo necesario.
📦
tsup sin --dts en paquetes
Sin la flag --dts, los consumidores del paquete no tienen tipos TypeScript. Siempre incluir en el build de packages.
Comandos de referencia rápida
# ─── DESARROLLO ─────────────────────────────────────────────────────
pnpm dev # arranca todas las apps en paralelo
pnpm dev --filter=web # solo apps/web
pnpm dev --filter=dashboard # solo el dashboard
# ─── BUILD ──────────────────────────────────────────────────────────
pnpm build # build de todo (Turborepo, cacheado)
pnpm build --filter=@cultiva/ui # solo el paquete UI
# ─── TESTS ──────────────────────────────────────────────────────────
pnpm test # tests de todo el monorepo
pnpm test --filter=@cultiva/ui # tests del paquete UI
# ─── AÑADIR DEPENDENCIAS ────────────────────────────────────────────
pnpm add react --filter=apps/web # dep en una app específica
pnpm add -Dw typescript # dep de desarrollo en la raíz
pnpm add @cultiva/ui --filter=apps/web # paquete interno
# ─── PUBLICACIÓN ────────────────────────────────────────────────────
pnpm changeset # crear changeset (dev)
pnpm changeset version # bumps de versión (CI)
pnpm changeset publish # publicar en npm (CI)
# ─── UTILIDADES ─────────────────────────────────────────────────────
pnpm turbo run build --graph # visualizar grafo de dependencias
pnpm turbo run build --dry-run # ver qué se ejecutaría sin correrlo
npx madge --circular apps/web/src # detectar deps circulares