claude-code-arcane 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/agents/engineering/e2e-tester.md +65 -0
- package/agents/engineering/nestjs-engineer.md +74 -0
- package/agents/engineering/nextjs-engineer.md +72 -0
- package/agents/engineering/nextjs-reviewer.md +40 -0
- package/dist/cli.js +105 -6
- package/docs/trusted-publishing-setup.md +163 -0
- package/package.json +1 -1
- package/profiles/backend-nestjs.yaml +58 -0
- package/profiles/backend-nextjs.yaml +56 -0
- package/rules/nestjs-code.md +73 -0
- package/rules/nextjs-code.md +60 -0
- package/rules/react-perf.md +59 -0
- package/skills/ai-sdk-setup/SKILL.md +82 -0
- package/skills/nestjs-best-practices/SKILL.md +70 -0
- package/skills/nestjs-best-practices/references/api-design.md +34 -0
- package/skills/nestjs-best-practices/references/architecture.md +53 -0
- package/skills/nestjs-best-practices/references/database.md +37 -0
- package/skills/nestjs-best-practices/references/dependency-injection.md +45 -0
- package/skills/nestjs-best-practices/references/error-handling.md +43 -0
- package/skills/nestjs-best-practices/references/microservices-devops.md +41 -0
- package/skills/nestjs-best-practices/references/performance.md +32 -0
- package/skills/nestjs-best-practices/references/security.md +50 -0
- package/skills/nestjs-best-practices/references/testing.md +32 -0
- package/skills/nestjs-scaffold/SKILL.md +75 -0
- package/skills/nextjs-best-practices/SKILL.md +64 -0
- package/skills/nextjs-best-practices/references/advanced.md +21 -0
- package/skills/nextjs-best-practices/references/bundle-size.md +32 -0
- package/skills/nextjs-best-practices/references/client-fetching.md +20 -0
- package/skills/nextjs-best-practices/references/javascript.md +47 -0
- package/skills/nextjs-best-practices/references/rendering.md +46 -0
- package/skills/nextjs-best-practices/references/rerender.md +55 -0
- package/skills/nextjs-best-practices/references/server.md +47 -0
- package/skills/nextjs-best-practices/references/waterfalls.md +41 -0
- package/skills/nextjs-scaffold/SKILL.md +76 -0
- package/skills/pgvector-search/SKILL.md +67 -0
- package/skills/seo-nextjs/SKILL.md +81 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 5. Performance (HIGH)
|
|
2
|
+
|
|
3
|
+
## 5.3 Optimize Database Queries — HIGH
|
|
4
|
+
|
|
5
|
+
Seleccionar solo columnas necesarias, índices apropiados, no over-fetch de relaciones.
|
|
6
|
+
```ts
|
|
7
|
+
// Prisma: select explícito en vez de traer la fila entera + relaciones
|
|
8
|
+
prisma.user.findMany({ select: { id: true, email: true } });
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 5.4 Use Caching Strategically — HIGH
|
|
12
|
+
|
|
13
|
+
Cachear operaciones caras con TTL apropiado e invalidación basada en eventos.
|
|
14
|
+
```ts
|
|
15
|
+
@UseInterceptors(CacheInterceptor)
|
|
16
|
+
@CacheTTL(30)
|
|
17
|
+
@Get('stats') getStats() { /* ... */ }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 5.1 Async Lifecycle Hooks Correctly — HIGH
|
|
21
|
+
|
|
22
|
+
Retornar promesas de hooks async; constructores síncronos (no bloquear el arranque).
|
|
23
|
+
```ts
|
|
24
|
+
async onModuleInit() { await this.warmupCache(); } // ✓
|
|
25
|
+
// constructor(){ await ... } ✗ no se puede / bloquea
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 5.2 Lazy Loading for Large Modules — MEDIUM
|
|
29
|
+
|
|
30
|
+
Diferir inicialización de módulos poco usados con `LazyModuleLoader` → menor tiempo de arranque y memoria.
|
|
31
|
+
|
|
32
|
+
_Ref: https://docs.nestjs.com/techniques/caching · https://docs.nestjs.com/fundamentals/lazy-loading-modules_
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# 4. Security (HIGH)
|
|
2
|
+
|
|
3
|
+
## 4.1 Secure JWT Authentication — CRITICAL
|
|
4
|
+
|
|
5
|
+
Tokens de vida corta (15m) + refresh tokens. Secreto en env/secret manager. Nunca data sensible en el payload.
|
|
6
|
+
```ts
|
|
7
|
+
JwtModule.register({
|
|
8
|
+
secret: process.env.JWT_SECRET, // nunca hardcodeado
|
|
9
|
+
signOptions: { expiresIn: '15m' },
|
|
10
|
+
});
|
|
11
|
+
// payload: { sub: userId, role } ← nada de passwords, emails completos, PII
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 4.5 Validate All Input with DTOs and Pipes — HIGH
|
|
15
|
+
|
|
16
|
+
`ValidationPipe` global + DTOs decorados. Rechazar input malformado antes de procesarlo.
|
|
17
|
+
```ts
|
|
18
|
+
app.useGlobalPipes(new ValidationPipe({
|
|
19
|
+
whitelist: true, // descarta props no declaradas
|
|
20
|
+
forbidNonWhitelisted: true, // 400 si vienen props extra
|
|
21
|
+
transform: true,
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
export class CreateUserDto {
|
|
25
|
+
@IsEmail() email: string;
|
|
26
|
+
@MinLength(8) password: string;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 4.4 Use Guards for AuthN/AuthZ — HIGH
|
|
31
|
+
|
|
32
|
+
Guards declarativos, no checks manuales en cada handler.
|
|
33
|
+
```ts
|
|
34
|
+
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
35
|
+
@Roles('admin')
|
|
36
|
+
@Get('users') findAll() { /* ... */ }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 4.2 Rate Limiting — HIGH
|
|
40
|
+
|
|
41
|
+
`@nestjs/throttler`, límites por endpoint (login más estricto que lecturas).
|
|
42
|
+
```ts
|
|
43
|
+
@Throttle({ default: { limit: 5, ttl: 60000 } }) // 5/min en /login
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 4.3 Sanitize Output to Prevent XSS — HIGH
|
|
47
|
+
|
|
48
|
+
Sanitizar contenido generado por usuario antes de almacenar; `Content-Type` correcto en respuestas.
|
|
49
|
+
|
|
50
|
+
_Ref: https://docs.nestjs.com/security/authentication · https://docs.nestjs.com/security/rate-limiting_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 6. Testing (MEDIUM-HIGH)
|
|
2
|
+
|
|
3
|
+
## 6.3 Use Testing Module for Unit Tests — HIGH
|
|
4
|
+
|
|
5
|
+
Entorno aislado con `Test.createTestingModule` y dependencias mockeadas.
|
|
6
|
+
```ts
|
|
7
|
+
const moduleRef = await Test.createTestingModule({
|
|
8
|
+
providers: [
|
|
9
|
+
OrdersService,
|
|
10
|
+
{ provide: UserRepository, useValue: mockUserRepo },
|
|
11
|
+
],
|
|
12
|
+
}).compile();
|
|
13
|
+
const service = moduleRef.get(OrdersService);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 6.2 Mock External Services in Tests — HIGH
|
|
17
|
+
|
|
18
|
+
Nunca llamar APIs o DBs reales en tests. Mockear todas las dependencias externas con escenarios realistas (éxito, error, timeout).
|
|
19
|
+
|
|
20
|
+
## 6.1 Use Supertest for E2E Testing — HIGH
|
|
21
|
+
|
|
22
|
+
Probar el ciclo completo request/response incluyendo middleware, guards y pipes. Setup/teardown apropiados.
|
|
23
|
+
```ts
|
|
24
|
+
const app = moduleRef.createNestApplication();
|
|
25
|
+
await app.init();
|
|
26
|
+
await request(app.getHttpServer())
|
|
27
|
+
.post('/users').send({ email: 'a@b.com', password: 'secret123' })
|
|
28
|
+
.expect(201);
|
|
29
|
+
await app.close();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
_Ref: https://docs.nestjs.com/fundamentals/testing_
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-scaffold
|
|
3
|
+
description: "Scaffold de API NestJS production-ready con módulos por feature, DI, ValidationPipe global, Prisma/Drizzle, guards de auth y testing. Usar para iniciar un backend NestJS nuevo."
|
|
4
|
+
category: "backend"
|
|
5
|
+
argument-hint: "[project-name]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nestjs-scaffold — NestJS API Scaffolder
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
**Antes de generar cualquier código, completar estos pasos en orden.**
|
|
14
|
+
|
|
15
|
+
### Step 0: Gather Requirements
|
|
16
|
+
1. **Nombre del servicio** (kebab-case)
|
|
17
|
+
2. **Transport:** REST (default) / GraphQL / microservicio
|
|
18
|
+
3. **ORM:** Prisma (default) / Drizzle / TypeORM
|
|
19
|
+
4. **DB:** PostgreSQL (default) / MySQL
|
|
20
|
+
5. **Auth:** JWT + refresh (default) / OAuth / ninguna
|
|
21
|
+
6. **Deploy:** Docker (default) / Railway / AWS ECS
|
|
22
|
+
|
|
23
|
+
### Step 1: Crear base
|
|
24
|
+
```bash
|
|
25
|
+
npm i -g @nestjs/cli
|
|
26
|
+
nest new <name> --package-manager pnpm
|
|
27
|
+
cd <name>
|
|
28
|
+
nest g module users && nest g controller users && nest g service users
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Step 2: Estructura (feature modules — NO por capa)
|
|
32
|
+
```
|
|
33
|
+
src/
|
|
34
|
+
users/ { users.module.ts, users.controller.ts, users.service.ts, dto/, entities/ }
|
|
35
|
+
auth/ { auth.module.ts, jwt.strategy.ts, guards/, decorators/ }
|
|
36
|
+
common/ { filters/, interceptors/, pipes/ }
|
|
37
|
+
config/ { config.module.ts, env.validation.ts }
|
|
38
|
+
main.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Step 3: Bootstrap no negociable (`main.ts`)
|
|
42
|
+
```ts
|
|
43
|
+
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }));
|
|
44
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
45
|
+
app.enableShutdownHooks(); // graceful shutdown
|
|
46
|
+
app.enableVersioning({ type: VersioningType.URI });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 4: Defaults
|
|
50
|
+
- Constructor injection siempre; cero `ModuleRef.get()` en runtime
|
|
51
|
+
- DTOs decorados (`class-validator`) en todos los endpoints
|
|
52
|
+
- Guards declarativos para auth/authz
|
|
53
|
+
- `ConfigModule` con validación de env (Joi/Zod) al boot
|
|
54
|
+
- Migraciones versionadas, `synchronize: false`
|
|
55
|
+
- Health check (`@nestjs/terminus`) + structured logging (pino)
|
|
56
|
+
- Tests: `Test.createTestingModule` (unit) + supertest (e2e)
|
|
57
|
+
|
|
58
|
+
### Step 5: Verificar
|
|
59
|
+
`npm run build` + `npm run test`. Revisar contra la rule `nestjs-code` y el skill `nestjs-best-practices`. Build + tests verdes → scaffold **READY**.
|
|
60
|
+
|
|
61
|
+
## Stack Defaults
|
|
62
|
+
|
|
63
|
+
| Componente | Default |
|
|
64
|
+
|------------|---------|
|
|
65
|
+
| Framework | NestJS 10+ |
|
|
66
|
+
| ORM | Prisma |
|
|
67
|
+
| DB | PostgreSQL |
|
|
68
|
+
| Auth | JWT + refresh tokens |
|
|
69
|
+
| Validación | class-validator + ValidationPipe |
|
|
70
|
+
| Testing | Jest + supertest |
|
|
71
|
+
| Deploy | Docker |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
_Inspirado en las skills NestJS de [kadajett/agent-nestjs-skills](https://github.com/kadajett/agent-nestjs-skills) y [giuseppe-trisciuoglio/developer-kit](https://github.com/giuseppe-trisciuoglio/developer-kit). Adaptado al formato Arcane._
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-best-practices
|
|
3
|
+
description: "React + Next.js performance: 64 reglas en 8 categorías (waterfalls, bundle, server, client fetching, re-render, rendering, JS, advanced) priorizadas por impacto, de Vercel Engineering. Usar al escribir/optimizar React o Next.js."
|
|
4
|
+
category: "frontend"
|
|
5
|
+
argument-hint: "[waterfalls|bundle|server|rerender|rendering|js|all]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nextjs-best-practices — Performance priorizada por impacto
|
|
10
|
+
|
|
11
|
+
64 reglas de optimización de performance para React y Next.js, de Vercel Engineering, organizadas en 8 categorías priorizadas de **CRITICAL** a **LOW**. Detalle por categoría en `references/`.
|
|
12
|
+
|
|
13
|
+
## MANDATORY WORKFLOW
|
|
14
|
+
|
|
15
|
+
**Diagnóstico dirigido por métricas, no aplicar las 64 reglas a ciegas** (este es el enfoque `vercel-optimize`: medir primero, optimizar solo lo que las métricas señalan).
|
|
16
|
+
|
|
17
|
+
### Step 0: Medir / localizar
|
|
18
|
+
|
|
19
|
+
1. **¿Hay métricas?** Lighthouse, Web Vitals (LCP/INP/CLS), bundle analyzer, traces de Vercel.
|
|
20
|
+
2. **¿Cuál es el síntoma dominante?** TTFB alto → waterfalls/server. Bundle grande / TBT → bundle size. Jank en interacción → re-render. Layout shift → rendering.
|
|
21
|
+
3. Identificar las rutas/componentes concretos que las métricas apuntan. Optimizar **esos**, no todo.
|
|
22
|
+
|
|
23
|
+
### Step 1: Priorizar por impacto
|
|
24
|
+
|
|
25
|
+
| Prioridad | Categoría | Impacto | Reference |
|
|
26
|
+
|-----------|-----------|---------|-----------|
|
|
27
|
+
| 1 | Eliminating Waterfalls | CRITICAL | `references/waterfalls.md` |
|
|
28
|
+
| 2 | Bundle Size Optimization | CRITICAL | `references/bundle-size.md` |
|
|
29
|
+
| 3 | Server-Side Performance | HIGH | `references/server.md` |
|
|
30
|
+
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `references/client-fetching.md` |
|
|
31
|
+
| 5 | Re-render Optimization | MEDIUM | `references/rerender.md` |
|
|
32
|
+
| 6 | Rendering Performance | MEDIUM | `references/rendering.md` |
|
|
33
|
+
| 7 | JavaScript Performance | LOW-MEDIUM | `references/javascript.md` |
|
|
34
|
+
| 8 | Advanced Patterns | LOW | `references/advanced.md` |
|
|
35
|
+
|
|
36
|
+
### Step 2: Aplicar + medir de nuevo
|
|
37
|
+
|
|
38
|
+
Aplicar la regla, re-medir. Si la métrica no mejoró, revertir y subir de categoría — no acumular micro-optimizaciones sin impacto medible.
|
|
39
|
+
|
|
40
|
+
### Step 3: Checklist de cierre
|
|
41
|
+
|
|
42
|
+
- [ ] Fetches independientes en paralelo (`Promise.all`), no en cascada
|
|
43
|
+
- [ ] Server Components por defecto; `'use client'` empujado abajo en el árbol
|
|
44
|
+
- [ ] Sin barrel imports en el critical path; componentes pesados con `next/dynamic`
|
|
45
|
+
- [ ] Server Actions autenticadas individualmente
|
|
46
|
+
- [ ] Sin componentes definidos dentro de componentes
|
|
47
|
+
- [ ] `next/image` + `generateMetadata` + resource hints donde corresponde
|
|
48
|
+
- [ ] Re-medir Web Vitals post-cambios
|
|
49
|
+
|
|
50
|
+
Si todo el checklist pasa → optimización **COMPLETE**. Antes de escribir cambios significativos, confirmar el approach con el usuario (Question → Decision → Approval).
|
|
51
|
+
|
|
52
|
+
## Tabla síntoma → categoría
|
|
53
|
+
|
|
54
|
+
| Métrica mala | Categoría |
|
|
55
|
+
|--------------|-----------|
|
|
56
|
+
| TTFB / tiempo de respuesta del server | Waterfalls, Server |
|
|
57
|
+
| First Load JS / TBT alto | Bundle Size |
|
|
58
|
+
| INP / jank en interacción | Re-render, JS |
|
|
59
|
+
| LCP | Rendering, Bundle, Server |
|
|
60
|
+
| CLS / flicker en hydration | Rendering |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
_Adaptado de [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) (react-best-practices, MIT, © Vercel). 64 reglas priorizadas por impacto, reorganizadas al formato skill+references de Arcane._
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 8. Advanced Patterns (LOW)
|
|
2
|
+
|
|
3
|
+
Patrones de borde con `useEffectEvent` y setup de app. Impacto bajo pero evitan bugs sutiles.
|
|
4
|
+
|
|
5
|
+
## 8.4 useEffectEvent for Stable Callback Refs — LOW
|
|
6
|
+
Referencias de callback estables para componentes memoizados sin re-disparar effects.
|
|
7
|
+
```tsx
|
|
8
|
+
const onTick = useEffectEvent(() => doSomething(latestProp));
|
|
9
|
+
useEffect(() => { const id = setInterval(onTick, 1000); return () => clearInterval(id); }, []);
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 8.1 Do Not Put Effect Events in Dependency Arrays — LOW
|
|
13
|
+
Los Effect Events no van en el array de dependencias; se llaman desde el effect, no lo re-disparan.
|
|
14
|
+
|
|
15
|
+
## 8.2 Initialize App Once, Not Per Mount — LOW
|
|
16
|
+
Setup de app a nivel module-load, no en effects/mounts de componente.
|
|
17
|
+
|
|
18
|
+
## 8.3 Store Event Handlers in Refs — LOW
|
|
19
|
+
Handlers que cambian seguido en refs → no causan re-render.
|
|
20
|
+
|
|
21
|
+
_Ref: https://react.dev/reference/react/experimental_useEffectEvent_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 2. Bundle Size Optimization (CRITICAL)
|
|
2
|
+
|
|
3
|
+
## 2.1 Avoid Barrel File Imports — CRITICAL
|
|
4
|
+
Los barrels (`index.ts` que re-exportan todo) arrastran miles de módulos sin usar.
|
|
5
|
+
```ts
|
|
6
|
+
// ✗ import { debounce } from 'lodash'; (todo lodash)
|
|
7
|
+
// ✓ import debounce from 'lodash/debounce';
|
|
8
|
+
```
|
|
9
|
+
Configurar `optimizePackageImports` en `next.config.js` para libs grandes.
|
|
10
|
+
|
|
11
|
+
## 2.4 Dynamic Imports for Heavy Components — CRITICAL
|
|
12
|
+
```tsx
|
|
13
|
+
const Chart = dynamic(() => import('./Chart'), { ssr: false, loading: () => <Skeleton/> });
|
|
14
|
+
```
|
|
15
|
+
Para charts, editores de texto rico, modales, mapas — todo lo no necesario en el primer render.
|
|
16
|
+
|
|
17
|
+
## 2.5 Prefer Statically Analyzable Paths — HIGH
|
|
18
|
+
Imports con paths literales/mapas explícitos, no construidos en runtime → permite tree-shaking.
|
|
19
|
+
|
|
20
|
+
## 2.2 Conditional Module Loading — HIGH
|
|
21
|
+
Cargar data/módulos grandes solo cuando la feature se activa.
|
|
22
|
+
|
|
23
|
+
## 2.6 Preload Based on User Intent — MEDIUM
|
|
24
|
+
Precargar el bundle pesado en hover/focus, antes de que se necesite.
|
|
25
|
+
```tsx
|
|
26
|
+
<button onMouseEnter={() => import('./HeavyModal')}>Abrir</button>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 2.3 Defer Non-Critical Third-Party Libraries — MEDIUM
|
|
30
|
+
Analytics, logging, error tracking → cargar después de hydration, no bloquear el bundle inicial (`next/script` con `strategy="afterInteractive"` o `lazyOnload`).
|
|
31
|
+
|
|
32
|
+
_Ref: https://nextjs.org/docs/app/api-reference/components/script · https://nextjs.org/docs/app/api-reference/config/next-config-js/optimizePackageImports_
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
|
2
|
+
|
|
3
|
+
## 4.3 Use SWR for Automatic Deduplication — MEDIUM-HIGH
|
|
4
|
+
Dedup de requests, caching y revalidación entre instancias de componente.
|
|
5
|
+
```ts
|
|
6
|
+
const { data } = useSWR(`/api/user/${id}`, fetcher); // mismo key = 1 request
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 4.2 Use Passive Event Listeners for Scrolling — MEDIUM
|
|
10
|
+
```ts
|
|
11
|
+
el.addEventListener('touchstart', handler, { passive: true }); // scroll inmediato
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 4.4 Version and Minimize localStorage Data — MEDIUM
|
|
15
|
+
Prefijo de versión en las keys (`app:v2:prefs`) y guardar solo campos necesarios → evita conflictos y reduce tamaño.
|
|
16
|
+
|
|
17
|
+
## 4.1 Deduplicate Global Event Listeners — LOW
|
|
18
|
+
Compartir listeners globales entre instancias con un `Map` + `useSWRSubscription()` en vez de uno por componente.
|
|
19
|
+
|
|
20
|
+
_Ref: https://swr.vercel.app_
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 7. JavaScript Performance (LOW-MEDIUM)
|
|
2
|
+
|
|
3
|
+
Micro-optimizaciones. Aplicar solo en hot paths comprobados por profiling — no preventivamente.
|
|
4
|
+
|
|
5
|
+
## 7.8 Early Length Check for Array Comparisons — MEDIUM-HIGH
|
|
6
|
+
Comparar `length` antes de operaciones caras (sort/deep-compare).
|
|
7
|
+
|
|
8
|
+
## 7.1 Avoid Layout Thrashing — MEDIUM
|
|
9
|
+
Agrupar escrituras de estilo, luego leer layout una vez → evita reflows síncronos forzados.
|
|
10
|
+
|
|
11
|
+
## 7.4 Cache Repeated Function Calls — MEDIUM
|
|
12
|
+
`Map` para memoizar resultados de llamadas repetidas con mismos inputs.
|
|
13
|
+
|
|
14
|
+
## 7.7 Defer Non-Critical Work with requestIdleCallback — MEDIUM
|
|
15
|
+
Analytics, logging, prefetch en periodos idle del browser.
|
|
16
|
+
|
|
17
|
+
## 7.2 Build Index Maps for Repeated Lookups — LOW-MEDIUM
|
|
18
|
+
`Map` para O(1) en vez de múltiples `.find()`.
|
|
19
|
+
|
|
20
|
+
## 7.3 Cache Property Access in Loops — LOW-MEDIUM
|
|
21
|
+
Cachear `obj.prop` antes del loop.
|
|
22
|
+
|
|
23
|
+
## 7.5 Cache Storage API Calls — LOW-MEDIUM
|
|
24
|
+
Cachear lecturas de `localStorage`/`sessionStorage` en memoria.
|
|
25
|
+
|
|
26
|
+
## 7.6 Combine Multiple Array Iterations — LOW-MEDIUM
|
|
27
|
+
Fusionar `.filter()`/`.map()` múltiples en un solo loop.
|
|
28
|
+
|
|
29
|
+
## 7.9 Early Return from Functions — LOW-MEDIUM
|
|
30
|
+
Retornar apenas el resultado está determinado.
|
|
31
|
+
|
|
32
|
+
## 7.10 Hoist RegExp Creation — LOW-MEDIUM
|
|
33
|
+
`RegExp` a nivel módulo, no recrear en cada llamada/render.
|
|
34
|
+
|
|
35
|
+
## 7.11 Use flatMap to Map and Filter in One Pass — LOW-MEDIUM
|
|
36
|
+
`.flatMap(x => cond ? [f(x)] : [])` en vez de `.map().filter(Boolean)`.
|
|
37
|
+
|
|
38
|
+
## 7.13 Use Set/Map for O(1) Lookups — LOW
|
|
39
|
+
`Set.has()` en vez de `.includes()`/`.indexOf()` repetidos.
|
|
40
|
+
|
|
41
|
+
## 7.12 Use Loop for Min/Max Instead of Sort — LOW
|
|
42
|
+
Loop O(n) para min/max en vez de sort O(n log n).
|
|
43
|
+
|
|
44
|
+
## 7.14 Use toSorted() Instead of sort() — LOW
|
|
45
|
+
`.toSorted()` para inmutabilidad (no muta el original).
|
|
46
|
+
|
|
47
|
+
_Ref: https://developer.mozilla.org/docs/Web/API/Window/requestIdleCallback_
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 6. Rendering Performance (MEDIUM)
|
|
2
|
+
|
|
3
|
+
## 6.2 CSS content-visibility for Long Lists — HIGH
|
|
4
|
+
```css
|
|
5
|
+
.row { content-visibility: auto; contain-intrinsic-size: 0 80px; }
|
|
6
|
+
```
|
|
7
|
+
Difiere render y paint de lo off-screen.
|
|
8
|
+
|
|
9
|
+
## 6.8 Use defer or async on Script Tags — HIGH
|
|
10
|
+
`defer`/`async` para no bloquear el render durante el parsing.
|
|
11
|
+
|
|
12
|
+
## 6.10 Use React DOM Resource Hints — HIGH
|
|
13
|
+
```ts
|
|
14
|
+
import { preload, preconnect } from 'react-dom';
|
|
15
|
+
preconnect('https://cdn.example.com');
|
|
16
|
+
preload('/hero.jpg', { as: 'image' });
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 6.5 Prevent Hydration Mismatch Without Flickering — MEDIUM
|
|
20
|
+
Script inline síncrono que actualiza el DOM antes de hidratar (ej: theme).
|
|
21
|
+
|
|
22
|
+
## 6.7 Use Activity Component for Show/Hide — MEDIUM
|
|
23
|
+
`<Activity>` preserva estado y DOM para componentes que togglean visibilidad seguido.
|
|
24
|
+
|
|
25
|
+
## 6.6 Suppress Expected Hydration Mismatches — LOW-MEDIUM
|
|
26
|
+
`suppressHydrationWarning` solo para diferencias server/client intencionales (fechas, IDs).
|
|
27
|
+
|
|
28
|
+
## 6.1 Animate SVG Wrapper Instead of SVG Element — LOW
|
|
29
|
+
Envolver el SVG en un div y animar el wrapper → aceleración por hardware.
|
|
30
|
+
|
|
31
|
+
## 6.3 Hoist Static JSX Elements — LOW
|
|
32
|
+
JSX estático fuera del componente → no se recrea en cada render.
|
|
33
|
+
|
|
34
|
+
## 6.4 Optimize SVG Precision — LOW
|
|
35
|
+
Reducir precisión de coordenadas SVG → menor tamaño, sin impacto visual.
|
|
36
|
+
|
|
37
|
+
## 6.9 Use Explicit Conditional Rendering — LOW
|
|
38
|
+
Ternario en vez de `&&` → evita renderizar `0` u otros falsy.
|
|
39
|
+
```tsx
|
|
40
|
+
{count > 0 ? <Badge n={count}/> : null} // no {count && ...}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 6.11 Use useTransition Over Manual Loading States — LOW
|
|
44
|
+
`useTransition` da pending state automático en vez de `useState` manual.
|
|
45
|
+
|
|
46
|
+
_Ref: https://developer.mozilla.org/docs/Web/CSS/content-visibility · https://react.dev/reference/react-dom/preload_
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 5. Re-render Optimization (MEDIUM)
|
|
2
|
+
|
|
3
|
+
## 5.4 Don't Define Components Inside Components — HIGH
|
|
4
|
+
Definirlos inline los remonta en cada render (pierde estado y DOM). Definir a nivel módulo o extraer.
|
|
5
|
+
```tsx
|
|
6
|
+
// ✗ function Parent(){ function Row(){...}; return <Row/> }
|
|
7
|
+
// ✓ Row a nivel módulo, fuera de Parent
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## 5.1 Calculate Derived State During Rendering — MEDIUM
|
|
11
|
+
Computar de props/state durante el render, no guardarlo en `useState` (se desincroniza).
|
|
12
|
+
```tsx
|
|
13
|
+
const fullName = `${first} ${last}`; // ✓ no useState + useEffect
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 5.11 Use Functional setState Updates — MEDIUM
|
|
17
|
+
`setCount(c => c + 1)` — opera sobre el último valor, callbacks estables.
|
|
18
|
+
|
|
19
|
+
## 5.12 Use Lazy State Initialization — MEDIUM
|
|
20
|
+
`useState(() => expensiveInit())` — no recomputa en cada render.
|
|
21
|
+
|
|
22
|
+
## 5.13 Use Transitions for Non-Urgent Updates — MEDIUM
|
|
23
|
+
`startTransition(() => setFilter(x))` — mantiene la UI responsiva.
|
|
24
|
+
|
|
25
|
+
## 5.14 useDeferredValue for Expensive Derived Renders — MEDIUM
|
|
26
|
+
Mantiene el input responsivo difiriendo el resultado pesado.
|
|
27
|
+
|
|
28
|
+
## 5.15 Use useRef for Transient Values — MEDIUM
|
|
29
|
+
Valores que cambian seguido y no son UI → `useRef`, no re-render.
|
|
30
|
+
|
|
31
|
+
## 5.2 Defer State Reads to Usage Point — MEDIUM
|
|
32
|
+
Leer state dinámico dentro del callback, no suscribirse en render time.
|
|
33
|
+
|
|
34
|
+
## 5.5 Extract Default Non-primitive Values — MEDIUM
|
|
35
|
+
Defaults objeto/función a constantes a nivel módulo → preserva memoización.
|
|
36
|
+
|
|
37
|
+
## 5.6 Extract to Memoized Components — MEDIUM
|
|
38
|
+
Aislar trabajo caro en componentes memoizados con early returns.
|
|
39
|
+
|
|
40
|
+
## 5.8 Put Interaction Logic in Event Handlers — MEDIUM
|
|
41
|
+
Side effects de interacción van en handlers, no en `useEffect`.
|
|
42
|
+
|
|
43
|
+
## 5.9 Split Combined Hook Computations — MEDIUM
|
|
44
|
+
Separar hooks con dependencias distintas → no recomputar lo independiente.
|
|
45
|
+
|
|
46
|
+
## 5.10 Subscribe to Derived State — MEDIUM
|
|
47
|
+
Suscribirse a un booleano derivado, no al valor continuo → menos renders.
|
|
48
|
+
|
|
49
|
+
## 5.3 Don't Wrap Simple Expressions in useMemo — LOW-MEDIUM
|
|
50
|
+
El overhead del hook supera el cómputo de un primitivo.
|
|
51
|
+
|
|
52
|
+
## 5.7 Narrow Effect Dependencies — LOW
|
|
53
|
+
Dependencias primitivas, no objetos enteros.
|
|
54
|
+
|
|
55
|
+
_Ref: https://react.dev/reference/react/useTransition · https://react.dev/learn/you-might-not-need-an-effect_
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 3. Server-Side Performance (HIGH)
|
|
2
|
+
|
|
3
|
+
## 3.1 Authenticate Server Actions Like API Routes — CRITICAL
|
|
4
|
+
Cada Server Action verifica auth/authz adentro, no solo confía en middleware.
|
|
5
|
+
```ts
|
|
6
|
+
'use server';
|
|
7
|
+
export async function deletePost(id: string) {
|
|
8
|
+
const session = await auth();
|
|
9
|
+
if (!session?.user) throw new Error('Unauthorized');
|
|
10
|
+
if (!canDelete(session.user, id)) throw new Error('Forbidden');
|
|
11
|
+
// ...
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 3.7 Parallel Data Fetching with Component Composition — CRITICAL
|
|
16
|
+
Componer para que fetches independientes corran a la vez (cada uno en su `<Suspense>`).
|
|
17
|
+
|
|
18
|
+
## 3.8 Parallel Nested Data Fetching — CRITICAL
|
|
19
|
+
Encadenar fetches dependientes dentro de la promesa de cada item → un item lento no bloquea a los demás.
|
|
20
|
+
|
|
21
|
+
## 3.3 Avoid Shared Module State for Request Data — HIGH
|
|
22
|
+
Variables mutables a nivel módulo se comparten entre requests → data leaks. Mantener datos de request locales al árbol de render.
|
|
23
|
+
|
|
24
|
+
## 3.5 Hoist Static I/O to Module Level — HIGH
|
|
25
|
+
Lecturas estáticas (config, archivos) a nivel módulo, no en cada request.
|
|
26
|
+
|
|
27
|
+
## 3.6 Minimize Serialization at RSC Boundaries — HIGH
|
|
28
|
+
Pasar solo los campos que el cliente usa, no el objeto entero.
|
|
29
|
+
|
|
30
|
+
## 3.4 Cross-Request LRU Caching — HIGH
|
|
31
|
+
LRU cache para datos compartidos entre requests dentro de una ventana de tiempo.
|
|
32
|
+
|
|
33
|
+
## 3.9 Per-Request Deduplication with React.cache() — MEDIUM
|
|
34
|
+
```ts
|
|
35
|
+
export const getUser = cache(async (id) => db.user.find(id)); // dedup mismo arg en el request
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 3.10 Use after() for Non-Blocking Operations — MEDIUM
|
|
39
|
+
```ts
|
|
40
|
+
import { after } from 'next/server';
|
|
41
|
+
after(() => logAnalytics(event)); // corre tras enviar la respuesta
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 3.2 Avoid Duplicate Serialization in RSC Props — LOW
|
|
45
|
+
Transformar en el cliente cuando evita duplicar referencias serializadas.
|
|
46
|
+
|
|
47
|
+
_Ref: https://nextjs.org/docs/app/building-your-application/data-fetching · https://react.dev/reference/react/cache_
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 1. Eliminating Waterfalls (CRITICAL)
|
|
2
|
+
|
|
3
|
+
El factor #1 de TTFB lento. Operaciones que esperan en serie cuando podrían correr en paralelo.
|
|
4
|
+
|
|
5
|
+
## 1.5 Promise.all() for Independent Operations — CRITICAL
|
|
6
|
+
```ts
|
|
7
|
+
// ✗ secuencial: 300ms + 300ms
|
|
8
|
+
const user = await getUser(id);
|
|
9
|
+
const posts = await getPosts(id);
|
|
10
|
+
// ✓ paralelo: 300ms
|
|
11
|
+
const [user, posts] = await Promise.all([getUser(id), getPosts(id)]);
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 1.4 Prevent Waterfall Chains in API Routes — CRITICAL
|
|
15
|
+
Iniciar operaciones independientes inmediatamente, await recién al usarlas.
|
|
16
|
+
```ts
|
|
17
|
+
const userP = getUser(id); // arranca ya
|
|
18
|
+
const settingsP = getSettings(id); // arranca ya
|
|
19
|
+
return { user: await userP, settings: await settingsP };
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 1.3 Dependency-Based Parallelization — CRITICAL
|
|
23
|
+
Cuando hay dependencias parciales, arrancar cada tarea en el momento más temprano posible (libs como `better-all` lo automatizan). No serializar todo por una sola dependencia.
|
|
24
|
+
|
|
25
|
+
## 1.7/3.7 Parallel Data Fetching with Component Composition — CRITICAL
|
|
26
|
+
Reestructurar componentes para que fetches independientes corran simultáneamente en vez de anidados.
|
|
27
|
+
|
|
28
|
+
## 1.6 Strategic Suspense Boundaries — HIGH
|
|
29
|
+
```tsx
|
|
30
|
+
<Suspense fallback={<Skeleton/>}>
|
|
31
|
+
<SlowData /> {/* streamea; el shell se muestra ya */}
|
|
32
|
+
</Suspense>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 1.1 Check Cheap Conditions Before Async Flags — HIGH
|
|
36
|
+
Evaluar guards síncronos antes de operaciones async para saltar trabajo en cold paths.
|
|
37
|
+
|
|
38
|
+
## 1.2 Defer Await Until Needed — HIGH
|
|
39
|
+
Mover el `await` a la rama donde realmente se usa, no bloquear ramas que no lo necesitan.
|
|
40
|
+
|
|
41
|
+
_Ref: https://react.dev/reference/react/Suspense_
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-scaffold
|
|
3
|
+
description: "Scaffold de app Next.js 15+ App Router con TypeScript estricto, shadcn/ui, Tailwind, RSC y estructura production-ready. Usar para iniciar un proyecto Next nuevo o agregar la base de UI."
|
|
4
|
+
category: "frontend"
|
|
5
|
+
argument-hint: "[project-name]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nextjs-scaffold — Next.js + shadcn/ui Scaffolder
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
**Antes de generar código, completar en orden.**
|
|
14
|
+
|
|
15
|
+
### Step 0: Gather Requirements
|
|
16
|
+
Clarificar (o inferir del contexto):
|
|
17
|
+
1. **Nombre del proyecto** (kebab-case)
|
|
18
|
+
2. **Router:** App Router (default) / Pages (legacy)
|
|
19
|
+
3. **Styling:** Tailwind + shadcn/ui (default) / CSS Modules
|
|
20
|
+
4. **Data layer:** Server Actions + RSC (default) / API routes / tRPC
|
|
21
|
+
5. **Auth:** Auth.js (NextAuth) / Clerk / ninguna
|
|
22
|
+
6. **DB:** Postgres + Prisma / Drizzle / ninguna
|
|
23
|
+
7. **Deploy:** Vercel (default) / Docker / self-host
|
|
24
|
+
|
|
25
|
+
Si ya está especificado, saltar al Step 1.
|
|
26
|
+
|
|
27
|
+
### Step 1: Crear base
|
|
28
|
+
```bash
|
|
29
|
+
npx create-next-app@latest <name> --ts --app --tailwind --eslint --src-dir --import-alias "@/*"
|
|
30
|
+
cd <name>
|
|
31
|
+
npx shadcn@latest init # style: default, base color: slate
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 2: Estructura
|
|
35
|
+
```
|
|
36
|
+
src/
|
|
37
|
+
app/ # App Router: layouts, pages, route handlers
|
|
38
|
+
(marketing)/ # route groups
|
|
39
|
+
api/ # route handlers solo si hace falta
|
|
40
|
+
components/
|
|
41
|
+
ui/ # shadcn primitives
|
|
42
|
+
lib/ # utils, db client, auth
|
|
43
|
+
server/ # server actions, data access (RSC-only)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Step 3: Defaults no negociables
|
|
47
|
+
- `tsconfig`: `"strict": true`, `noUncheckedIndexedAccess`
|
|
48
|
+
- **Server Components por defecto**; `'use client'` solo en hojas interactivas
|
|
49
|
+
- `next/image` + `generateMetadata` en cada page
|
|
50
|
+
- Env vars validadas con Zod (`src/lib/env.ts`)
|
|
51
|
+
- ESLint flat config + `@typescript-eslint/no-floating-promises`
|
|
52
|
+
|
|
53
|
+
### Step 4: Agregar piezas (invocar skills relacionadas)
|
|
54
|
+
- SEO → skill `seo-nextjs`
|
|
55
|
+
- Búsqueda semántica → skill `pgvector-search`
|
|
56
|
+
- Features de IA → skill `ai-sdk-setup`
|
|
57
|
+
- Performance → skill `nextjs-best-practices`
|
|
58
|
+
|
|
59
|
+
### Step 5: Verificar
|
|
60
|
+
`npm run build` limpio + `npx next lint`. Revisar contra las rules `nextjs-code` y `react-perf`. Build limpio + lint sin errores → scaffold **READY**.
|
|
61
|
+
|
|
62
|
+
## Stack Defaults
|
|
63
|
+
|
|
64
|
+
| Componente | Default |
|
|
65
|
+
|------------|---------|
|
|
66
|
+
| Next.js | 15+ App Router |
|
|
67
|
+
| Lenguaje | TypeScript estricto |
|
|
68
|
+
| UI | shadcn/ui + Tailwind |
|
|
69
|
+
| Data | Server Actions + RSC |
|
|
70
|
+
| Validación | Zod |
|
|
71
|
+
| ORM | Prisma |
|
|
72
|
+
| Deploy | Vercel |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
_Inspirado en `nextjs-shadcn` de [laguagu/claude-code-nextjs-skills](https://github.com/laguagu/claude-code-nextjs-skills). Adaptado al formato Arcane._
|