feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)

NOVAS SKILLS:
- next-best-practices      v0.1.0  (CLEAN) — Next.js App Router, RSC, caching, data
- nextjs-patterns          v1.0.0  (CLEAN) — Next.js 15: Server Actions, route handlers
- vite                     v1.0.0  (CLEAN) — env vars, aliases, proxy, CJS compat
- uncle-bob                v1.0.0  (CLEAN) — Clean Code, SOLID, Clean Architecture
- clean-code-review        v1.0.0  (CLEAN) — naming, guard clauses, anti-patterns, refactoring
- vue                      v1.0.0  (CLEAN) — Vue framework
- vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity

BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos):
- typescript/ — TS safe + generics gotchas
- react/ — Next.js App Router + Vite config
- vue/ — Composition API + Pinia
- linux/ — System diagnostic cheatsheet
- database/ — PostgreSQL + MySQL patterns
- browser/ — Chromium CLI + E2E testing
- security/ — SAST audit (OWASP Top 10)
- best-practices/ — Clean Code + SOLID + Clean Architecture
- deploy/ — Docker multi-stack + OpenClaw ops
- + INDEX.md como guia de navegacao

.learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
Pulse
2026-05-19 21:03:25 -03:00
parent 22d9f5b21d
commit ae39e45460
83 changed files with 13349 additions and 1 deletions
@@ -0,0 +1,8 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "nextjs-patterns",
"installedVersion": "1.0.0",
"installedAt": 1779235115388,
"fingerprint": "bc26850b7f1dac7a77618c50aab5563296d26699e6b00b8a31bab25e950849b4"
}
+145
View File
@@ -0,0 +1,145 @@
---
name: nextjs-patterns
description: >
Apply Next.js 15 best practices and modern patterns including App Router,
Server Components, Server Actions, caching strategies, and performance
optimization. Use when building or reviewing Next.js 15 applications to
ensure idiomatic, production-ready code.
---
# Next.js 15 Best Practices
## Core Principles
1. **Default to Server Components** — only add `"use client"` when you need interactivity or browser APIs
2. **Colocate by feature** — keep components, hooks, and utils near the routes that use them
3. **Type everything** — leverage TypeScript with strict mode enabled
4. **Cache deliberately** — understand the four caching layers and opt in/out explicitly
## Project Structure
```
app/
(marketing)/ # route group: no URL segment
page.tsx
(dashboard)/
layout.tsx
[id]/
page.tsx
api/
route.ts
components/
ui/ # shared, "dumb" UI components
features/ # feature-specific components
lib/
db.ts # database client (singleton)
auth.ts # auth helpers
utils.ts
```
## Server vs. Client Components
```tsx
// ✅ Server Component (default) — runs on server, no JS sent to client
export default async function ProductList() {
const products = await db.product.findMany()
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
// ✅ Client Component — only when needed
"use client"
import { useState } from "react"
export function Counter() {
const [n, setN] = useState(0)
return <button onClick={() => setN(n + 1)}>{n}</button>
}
```
## Data Fetching Patterns
```tsx
// Parallel fetching (avoid sequential waterfalls)
export default async function Dashboard() {
const [user, stats] = await Promise.all([
fetchUser(),
fetchStats(),
])
return <View user={user} stats={stats} />
}
// fetch with cache control
const data = await fetch("https://api.example.com/data", {
next: { revalidate: 60 }, // ISR: revalidate every 60s
// cache: "no-store" // always fresh
// cache: "force-cache" // static, until manual revalidation
})
```
## Server Actions
```tsx
// app/actions.ts
"use server"
import { revalidatePath } from "next/cache"
export async function createPost(formData: FormData) {
const title = formData.get("title") as string
await db.post.create({ data: { title } })
revalidatePath("/posts")
}
// app/posts/new/page.tsx
import { createPost } from "../actions"
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
)
}
```
## Metadata & SEO
```tsx
// Static metadata
export const metadata = {
title: "My App",
description: "...",
}
// Dynamic metadata
export async function generateMetadata({ params }) {
const post = await fetchPost(params.slug)
return { title: post.title, description: post.excerpt }
}
```
## Error & Loading States
```tsx
// app/posts/loading.tsx — automatic Suspense boundary
export default function Loading() {
return <Skeleton />
}
// app/posts/error.tsx — automatic error boundary
"use client"
export default function Error({ error, reset }) {
return <button onClick={reset}>Retry: {error.message}</button>
}
```
## Performance Checklist
- [ ] Images use `next/image` with explicit `width`/`height`
- [ ] Fonts use `next/font` (zero layout shift)
- [ ] Dynamic imports for heavy client components: `dynamic(() => import(...))`
- [ ] `generateStaticParams` for known dynamic routes
- [ ] Bundle analyzer run: `ANALYZE=true next build`
- [ ] Partial Prerendering (PPR) considered for mixed static/dynamic pages
## References
See `references/` folder for routing patterns, caching deep-dive, and migration guide.
+6
View File
@@ -0,0 +1,6 @@
{
"ownerId": "kn753sxg7hag86tcm2dtgzyhns84chhv",
"slug": "nextjs-patterns",
"version": "1.0.0",
"publishedAt": 1776047751623
}
@@ -0,0 +1,81 @@
# Next.js 15 Caching Deep Dive
## The Four Caching Layers
### 1. Request Memoization (in-memory, per request)
Automatically deduplicates identical `fetch()` calls within a single render tree.
```tsx
// Both components call fetchUser(id) — only ONE network request is made
async function Avatar({ id }) { const u = await fetchUser(id); return <img src={u.avatar} /> }
async function Name({ id }) { const u = await fetchUser(id); return <span>{u.name}</span> }
```
### 2. Data Cache (persistent, cross-request)
`fetch()` responses are stored on the server and reused across requests and deployments.
```tsx
// Static — cached indefinitely
fetch(url)
fetch(url, { cache: "force-cache" })
// Time-based revalidation (ISR)
fetch(url, { next: { revalidate: 3600 } }) // revalidate every hour
// Always fresh — no caching
fetch(url, { cache: "no-store" })
fetch(url, { next: { revalidate: 0 } })
```
### 3. Full Route Cache (build-time static rendering)
Pages rendered at build time are stored as static HTML+RSC payload.
```tsx
// Force dynamic rendering for this route
export const dynamic = "force-dynamic"
// Custom revalidation period for the whole route
export const revalidate = 60
```
### 4. Router Cache (client-side, per session)
Browser caches RSC payloads for instant back/forward navigation.
- Static routes: cached for 5 minutes
- Dynamic routes: cached for 30 seconds
## On-Demand Revalidation
```ts
// Revalidate by path
import { revalidatePath } from "next/cache"
revalidatePath("/blog")
revalidatePath("/blog/[slug]", "page")
// Revalidate by cache tag
import { revalidateTag } from "next/cache"
revalidateTag("posts")
// Tagging fetches
fetch(url, { next: { tags: ["posts"] } })
```
## unstable_cache for Non-Fetch Data
```ts
import { unstable_cache } from "next/cache"
const getCachedUser = unstable_cache(
async (id: string) => db.user.findUnique({ where: { id } }),
["user"],
{ revalidate: 300, tags: ["users"] }
)
```
## Common Pitfalls
| Mistake | Fix |
|---------|-----|
| Using `fetch` inside `useEffect` | Move to Server Component or use React Query |
| Forgetting `revalidatePath` after mutations | Call in Server Action after every write |
| Over-caching user-specific data | Add `cache: "no-store"` or check `cookies()`/`headers()` |
| Sequential awaits | Use `Promise.all()` for parallel fetching |
@@ -0,0 +1,104 @@
# Next.js 15 Migration & Configuration Guide
## Upgrading to Next.js 15
```bash
npx @next/codemod@canary upgrade latest
# or manual:
npm install next@latest react@latest react-dom@latest
```
## Key Breaking Changes (14 → 15)
### 1. Async Request APIs (Breaking)
`cookies()`, `headers()`, `params`, `searchParams` are now async:
```tsx
// Before (Next.js 14)
export default function Page({ params }) {
const { id } = params
}
// After (Next.js 15)
export default async function Page({ params }) {
const { id } = await params
}
// cookies and headers
import { cookies, headers } from "next/headers"
const cookieStore = await cookies()
const headersList = await headers()
```
### 2. Caching Defaults Changed
`fetch()` no longer caches by default (was `force-cache`, now `no-store`).
Add `{ cache: "force-cache" }` or set route-level `export const fetchCache = "default-cache"` to restore.
### 3. React 19 Compatibility
Next.js 15 supports React 19. New hooks available:
- `useActionState` (replaces `useFormState`)
- `useFormStatus`
- `use()` for reading promises/context
## next.config.ts (TypeScript config)
```ts
import type { NextConfig } from "next"
const config: NextConfig = {
experimental: {
ppr: "incremental", // Partial Prerendering
reactCompiler: true, // React Compiler (auto-memoization)
},
images: {
remotePatterns: [
{ protocol: "https", hostname: "images.example.com" },
],
},
logging: {
fetches: { fullUrl: true }, // Log all fetch calls in dev
},
}
export default config
```
## Environment Variables
```bash
# .env.local
DATABASE_URL="postgresql://..."
NEXT_PUBLIC_API_URL="https://api.example.com" # exposed to browser
```
```ts
// Access server-side
process.env.DATABASE_URL
// Access client-side (only NEXT_PUBLIC_ prefix)
process.env.NEXT_PUBLIC_API_URL
```
## Turbopack (Default in Dev)
```bash
next dev # uses Turbopack by default in Next.js 15
next dev --turbopack # explicit flag (same behavior)
next build # Webpack still default for production builds
```
## TypeScript Path Aliases
```json
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@/components/*": ["./components/*"],
"@/lib/*": ["./lib/*"]
}
}
}
```
@@ -0,0 +1,122 @@
# Next.js 15 Routing Patterns
## App Router Fundamentals
| File | Purpose |
|---------------|----------------------------------------------|
| `page.tsx` | Unique UI for a route, makes it publicly accessible |
| `layout.tsx` | Shared UI; does NOT re-render on navigation |
| `template.tsx`| Like layout but re-renders on navigation |
| `loading.tsx` | Instant loading state (React Suspense) |
| `error.tsx` | Error UI boundary (must be Client Component) |
| `not-found.tsx`| 404 UI |
| `route.ts` | API endpoint (GET, POST, etc.) |
## Route Groups
```
app/
(auth)/
login/page.tsx → /login
register/page.tsx → /register
(app)/
layout.tsx ← shared auth-required layout
dashboard/page.tsx → /dashboard
```
Route groups `(name)` organize routes without affecting the URL.
## Dynamic Routes
```tsx
// app/blog/[slug]/page.tsx
export default function Post({ params }: { params: { slug: string } }) {
return <h1>{params.slug}</h1>
}
// Generate static routes at build time
export async function generateStaticParams() {
const posts = await fetchAllPosts()
return posts.map(p => ({ slug: p.slug }))
}
```
## Catch-All & Optional Catch-All
```
app/shop/[...categories]/page.tsx → /shop/a/b/c
app/shop/[[...categories]]/page.tsx → /shop AND /shop/a/b/c
```
## Parallel Routes (Advanced)
```
app/
@team/page.tsx
@analytics/page.tsx
layout.tsx ← receives { team, analytics } props
```
```tsx
// layout.tsx
export default function Layout({ children, team, analytics }) {
return (
<div>
{children}
<aside>{team}</aside>
<aside>{analytics}</aside>
</div>
)
}
```
## Intercepting Routes
```
app/
feed/page.tsx
(..)photo/[id]/page.tsx ← intercepts /photo/[id] when navigated from /feed
photo/[id]/page.tsx ← full page on direct URL visit
```
Useful for modals that maintain background context.
## Middleware
```ts
// middleware.ts (project root)
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export function middleware(request: NextRequest) {
const token = request.cookies.get("token")
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ["/dashboard/:path*", "/api/protected/:path*"],
}
```
## API Routes (Route Handlers)
```ts
// app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server"
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const page = searchParams.get("page") ?? "1"
const posts = await fetchPosts(parseInt(page))
return NextResponse.json({ posts })
}
export async function POST(request: NextRequest) {
const body = await request.json()
const post = await createPost(body)
return NextResponse.json(post, { status: 201 })
}
```