Turborepo es un sistema de build para monorepos que entiende el grafo de dependencias entre paquetes y los compila en el orden correcto, paralelizando todo lo que puede y cacheando los resultados. Combinado con pnpm workspaces y TypeScript, es la solución más usada para gestionar monorepos con paquetes compartidos tipados.
Estructura del monorepo
// Estructura de carpetas: // mi-monorepo/ // apps/ // web/ (Next.js) // api/ (Node.js/Express) // packages/ // ui/ (@mi-org/ui) // utils/ (@mi-org/utils) // tsconfig/ (@mi-org/tsconfig) // package.json (raíz) // pnpm-workspace.yaml // turbo.json // pnpm-workspace.yaml // packages: // - "apps/*" // - "packages/*"
tsconfig base reutilizable
// packages/tsconfig/base.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"exclude": ["node_modules"]
}
// packages/tsconfig/nextjs.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["dom", "dom.iterable", "ES2022"],
"jsx": "preserve",
"allowJs": true
}
}
// Uso en apps/web/tsconfig.json:
{
"extends": "@mi-org/tsconfig/nextjs.json",
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
Paquete compartido tipado (@mi-org/utils)
// packages/utils/package.json
{
"name": "@mi-org/utils",
"version": "0.0.1",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch"
}
}
// packages/utils/src/index.ts
export function formatearFecha(fecha: Date, locale: string = 'es-ES'): string {
return new Intl.DateTimeFormat(locale, {
day: '2-digit', month: '2-digit', year: 'numeric',
}).format(fecha);
}
export function agrupar<T, K extends string | number>(
items: T[],
fn: (item: T) => K
): Record<K, T[]> {
return items.reduce((acc, item) => {
const clave = fn(item);
acc[clave] = acc[clave] ?? [];
acc[clave].push(item);
return acc;
}, {} as Record<K, T[]>);
}
Pipeline de builds con dependencias
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"], // ^ = dependencias del paquete primero
"outputs": ["dist/**", ".next/**"],
"cache": true
},
"dev": {
"dependsOn": ["^build"],
"cache": false,
"persistent": true
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"cache": true
},
"lint": {
"outputs": []
}
}
}
Usar el paquete compartido en una app
// apps/web/package.json
{
"dependencies": {
"@mi-org/utils": "workspace:*",
"@mi-org/ui": "workspace:*"
}
}
// apps/web/src/app/page.tsx
import { formatearFecha, agrupar } from '@mi-org/utils';
// TypeScript encuentra los tipos desde packages/utils/dist/index.d.ts
// (o desde el source si se configura paths)
const fecha = formatearFecha(new Date()); // string
Remote caching en Vercel
Turborepo integra un sistema de caché remota. Conectarlo a Vercel Remote Cache permite compartir los resultados de build entre máquinas CI y desarrolladores:
// Conectar con Vercel: // npx turbo login // npx turbo link // Con el token en CI: // TURBO_TOKEN=... TURBO_TEAM=... npx turbo build
Imagen: Pexels / Nemuel Sereti
