opencastle 0.32.5 → 0.32.6
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/README.md +13 -3
- package/bin/cli.mjs +2 -0
- package/package.json +1 -1
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/orchestrator/agents/api-designer.agent.md +25 -34
- package/src/orchestrator/agents/architect.agent.md +40 -84
- package/src/orchestrator/agents/content-engineer.agent.md +29 -31
- package/src/orchestrator/agents/copywriter.agent.md +35 -60
- package/src/orchestrator/agents/data-expert.agent.md +24 -30
- package/src/orchestrator/agents/database-engineer.agent.md +26 -31
- package/src/orchestrator/agents/developer.agent.md +32 -34
- package/src/orchestrator/agents/devops-expert.agent.md +31 -26
- package/src/orchestrator/agents/documentation-writer.agent.md +29 -29
- package/src/orchestrator/agents/performance-expert.agent.md +36 -33
- package/src/orchestrator/agents/release-manager.agent.md +25 -34
- package/src/orchestrator/agents/researcher.agent.md +41 -95
- package/src/orchestrator/agents/reviewer.agent.md +24 -34
- package/src/orchestrator/agents/security-expert.agent.md +35 -39
- package/src/orchestrator/agents/seo-specialist.agent.md +25 -32
- package/src/orchestrator/agents/session-guard.agent.md +20 -79
- package/src/orchestrator/agents/team-lead.agent.md +50 -254
- package/src/orchestrator/agents/testing-expert.agent.md +37 -49
- package/src/orchestrator/agents/ui-ux-expert.agent.md +33 -39
- package/src/orchestrator/customizations/KNOWN-ISSUES.md +0 -1
- package/src/orchestrator/customizations/agents/skill-matrix.json +12 -0
- package/src/orchestrator/instructions/general.instructions.md +24 -84
- package/src/orchestrator/plugins/astro/SKILL.md +23 -179
- package/src/orchestrator/plugins/convex/SKILL.md +38 -12
- package/src/orchestrator/plugins/netlify/SKILL.md +17 -13
- package/src/orchestrator/plugins/nextjs/SKILL.md +55 -261
- package/src/orchestrator/plugins/nx/SKILL.md +20 -72
- package/src/orchestrator/plugins/playwright/SKILL.md +5 -17
- package/src/orchestrator/plugins/slack/SKILL.md +28 -190
- package/src/orchestrator/plugins/teams/SKILL.md +10 -140
- package/src/orchestrator/plugins/vitest/SKILL.md +2 -2
- package/src/orchestrator/prompts/bug-fix.prompt.md +25 -63
- package/src/orchestrator/prompts/implement-feature.prompt.md +29 -66
- package/src/orchestrator/prompts/quick-refinement.prompt.md +31 -66
- package/src/orchestrator/skills/accessibility-standards/SKILL.md +50 -105
- package/src/orchestrator/skills/agent-hooks/SKILL.md +60 -110
- package/src/orchestrator/skills/agent-memory/SKILL.md +44 -93
- package/src/orchestrator/skills/api-patterns/SKILL.md +20 -68
- package/src/orchestrator/skills/code-commenting/SKILL.md +49 -101
- package/src/orchestrator/skills/context-map/SKILL.md +47 -88
- package/src/orchestrator/skills/data-engineering/SKILL.md +27 -74
- package/src/orchestrator/skills/decomposition/SKILL.md +50 -98
- package/src/orchestrator/skills/deployment-infrastructure/SKILL.md +44 -107
- package/src/orchestrator/skills/documentation-standards/SKILL.md +28 -89
- package/src/orchestrator/skills/fast-review/SKILL.md +51 -276
- package/src/orchestrator/skills/frontend-design/SKILL.md +53 -163
- package/src/orchestrator/skills/git-workflow/SKILL.md +18 -54
- package/src/orchestrator/skills/memory-merger/SKILL.md +51 -88
- package/src/orchestrator/skills/observability-logging/SKILL.md +29 -75
- package/src/orchestrator/skills/orchestration-protocols/SKILL.md +58 -117
- package/src/orchestrator/skills/panel-majority-vote/SKILL.md +65 -140
- package/src/orchestrator/skills/performance-optimization/SKILL.md +21 -85
- package/src/orchestrator/skills/project-consistency/SKILL.md +62 -281
- package/src/orchestrator/skills/react-development/SKILL.md +38 -86
- package/src/orchestrator/skills/security-hardening/SKILL.md +40 -84
- package/src/orchestrator/skills/self-improvement/SKILL.md +26 -60
- package/src/orchestrator/skills/seo-patterns/SKILL.md +40 -105
- package/src/orchestrator/skills/session-checkpoints/SKILL.md +26 -68
- package/src/orchestrator/skills/team-lead-reference/SKILL.md +66 -206
- package/src/orchestrator/skills/testing-workflow/SKILL.md +42 -112
- package/src/orchestrator/skills/validation-gates/SKILL.md +39 -170
- package/src/orchestrator/snippets/base-output-contract.md +14 -0
- package/src/orchestrator/snippets/discovered-issues-policy.md +15 -0
- package/src/orchestrator/snippets/logging-mandatory.md +11 -0
- package/src/orchestrator/snippets/never-expose-secrets.md +22 -0
|
@@ -16,149 +16,93 @@ description: "Next.js framework best practices covering App Router, server/clien
|
|
|
16
16
|
│ ├── loading.tsx # Loading UI (Suspense boundary)
|
|
17
17
|
│ ├── error.tsx # Error boundary (Client Component)
|
|
18
18
|
│ ├── not-found.tsx # 404 page
|
|
19
|
-
│ ├── global-error.tsx # Root error boundary
|
|
20
19
|
│ ├── (marketing)/ # Route group (no URL segment)
|
|
21
|
-
│ │
|
|
22
|
-
│ │ └── blog/page.tsx # → /blog
|
|
20
|
+
│ │ └── about/page.tsx # → /about
|
|
23
21
|
│ ├── dashboard/
|
|
24
22
|
│ │ ├── layout.tsx # Nested layout
|
|
25
|
-
│ │ ├── page.tsx # → /dashboard
|
|
26
23
|
│ │ └── [id]/page.tsx # → /dashboard/:id
|
|
27
24
|
│ └── api/
|
|
28
25
|
│ └── users/route.ts # API route handler
|
|
29
26
|
├── components/ # Shared React components
|
|
30
27
|
├── lib/ # Utilities, helpers, server logic
|
|
31
|
-
├── public/ # Static assets
|
|
28
|
+
├── public/ # Static assets
|
|
32
29
|
├── next.config.ts # Next.js configuration
|
|
33
30
|
├── middleware.ts # Edge middleware
|
|
34
31
|
└── .env.local # Environment variables (not committed)
|
|
35
32
|
```
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
- **Private Folders** `_internal` — opt out of routing entirely.
|
|
39
|
-
- **Parallel Routes** `@modal` — render multiple pages in the same layout.
|
|
40
|
-
- **Intercepting Routes** `(.)photo` — intercept navigation to show modals.
|
|
34
|
+
Route Groups `(name)` organize routes without affecting the URL. Private Folders `_internal` opt out of routing. Parallel Routes `@modal` render multiple pages in the same layout.
|
|
41
35
|
|
|
42
36
|
## Rendering Strategies
|
|
43
37
|
|
|
44
|
-
Next.js supports multiple rendering strategies per route:
|
|
45
|
-
|
|
46
38
|
| Strategy | When | How |
|
|
47
39
|
|----------|------|-----|
|
|
48
40
|
| **Static (SSG)** | Build time | Default for pages with no dynamic data |
|
|
49
|
-
| **
|
|
50
|
-
| **
|
|
51
|
-
| **
|
|
41
|
+
| **ISR** | Build + revalidation | `fetch` with `next: { revalidate: N }` or route segment config |
|
|
42
|
+
| **SSR** | Every request | `export const dynamic = 'force-dynamic'` or dynamic functions |
|
|
43
|
+
| **CSR** | Browser | `'use client'` components with `useEffect`/SWR |
|
|
52
44
|
| **Streaming** | Progressive | `<Suspense>` boundaries + `loading.tsx` |
|
|
53
|
-
| **
|
|
54
|
-
|
|
55
|
-
### Route Segment Config
|
|
56
|
-
|
|
57
|
-
Control per-route rendering behavior:
|
|
45
|
+
| **PPR** | Build + streaming | Static shell with dynamic holes via `<Suspense>` |
|
|
58
46
|
|
|
59
|
-
|
|
60
|
-
// app/dashboard/page.tsx
|
|
61
|
-
export const dynamic = 'force-dynamic'; // SSR every request
|
|
62
|
-
export const revalidate = 60; // ISR: revalidate every 60s
|
|
63
|
-
export const fetchCache = 'default-cache'; // Cache fetch requests
|
|
64
|
-
export const runtime = 'nodejs'; // 'nodejs' | 'edge'
|
|
65
|
-
```
|
|
47
|
+
Route segment config: `export const dynamic = 'force-dynamic'`, `export const revalidate = 60`, `export const runtime = 'edge'`.
|
|
66
48
|
|
|
67
49
|
## Server and Client Components
|
|
68
50
|
|
|
69
|
-
**Default: Server Components** — data fetching, heavy logic, non-interactive UI.
|
|
70
|
-
|
|
51
|
+
**Default: Server Components** — data fetching, heavy logic, non-interactive UI.
|
|
71
52
|
**Client Components** — add `'use client'` at top. Use for interactivity, state, browser APIs.
|
|
72
53
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| Use `useState` / `useEffect` | Client | React hooks need client runtime |
|
|
81
|
-
| Access browser APIs (localStorage, geolocation) | Client | Not available on server |
|
|
82
|
-
| Render static/non-interactive content | Server | Smaller bundle, faster paint |
|
|
83
|
-
| Show loading spinners for async children | Server (with `<Suspense>`) | Streams HTML progressively |
|
|
54
|
+
| Need | Component Type |
|
|
55
|
+
|------|---------------|
|
|
56
|
+
| Fetch data / read cookies/headers | Server |
|
|
57
|
+
| Interactive UI (clicks, inputs) | Client |
|
|
58
|
+
| `useState` / `useEffect` / browser APIs | Client |
|
|
59
|
+
| Static/non-interactive content | Server |
|
|
60
|
+
| Async children with loading state | Server + `<Suspense>` |
|
|
84
61
|
|
|
85
62
|
### Critical Rule
|
|
86
63
|
|
|
87
|
-
**Never use `next/dynamic` with `{ ssr: false }` inside a Server Component.**
|
|
64
|
+
**Never use `next/dynamic` with `{ ssr: false }` inside a Server Component.** Extract to a dedicated `'use client'` component and import it normally.
|
|
88
65
|
|
|
89
66
|
## Data Fetching
|
|
90
67
|
|
|
91
|
-
### Server-Side
|
|
92
|
-
|
|
93
|
-
Fetch directly in `async` Server Components. Next.js deduplicates identical `fetch` calls.
|
|
68
|
+
### Server-Side
|
|
94
69
|
|
|
95
70
|
```tsx
|
|
96
71
|
export default async function ProjectsPage() {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
}).then((res) => res.json());
|
|
100
|
-
return <ul>{projects.map((p: { id: string; name: string }) => <li key={p.id}>{p.name}</li>)}</ul>;
|
|
72
|
+
const data = await fetch('/api/projects', { next: { revalidate: 60 } }).then((r) => r.json());
|
|
73
|
+
return <ul>{data.map((p: { id: string; name: string }) => <li key={p.id}>{p.name}</li>)}</ul>;
|
|
101
74
|
}
|
|
102
75
|
```
|
|
103
76
|
|
|
104
|
-
### Server Actions
|
|
105
|
-
|
|
106
|
-
Define with `'use server'`. Call from Client Components via `action` or `startTransition`.
|
|
77
|
+
### Server Actions
|
|
107
78
|
|
|
108
79
|
```tsx
|
|
109
|
-
// lib/actions.ts
|
|
110
80
|
'use server';
|
|
111
81
|
import { revalidatePath } from 'next/cache';
|
|
112
82
|
export async function createItem(formData: FormData) {
|
|
113
|
-
|
|
114
|
-
await db.items.create({ data: { name } });
|
|
83
|
+
await db.items.create({ data: { name: formData.get('name') as string } });
|
|
115
84
|
revalidatePath('/items');
|
|
116
85
|
}
|
|
117
86
|
```
|
|
118
87
|
|
|
119
|
-
|
|
120
|
-
// components/CreateItemForm.tsx
|
|
121
|
-
'use client';
|
|
122
|
-
import { createItem } from '@/lib/actions';
|
|
123
|
-
export default function CreateItemForm() {
|
|
124
|
-
return <form action={createItem}><input name="name" required /><button type="submit">Add</button></form>;
|
|
125
|
-
}
|
|
126
|
-
```
|
|
88
|
+
Use from a Client Component: `<form action={createItem}>...</form>`
|
|
127
89
|
|
|
128
|
-
### Parallel
|
|
129
|
-
|
|
130
|
-
Initiate independent fetches simultaneously — never `await` sequentially.
|
|
90
|
+
### Parallel Fetching
|
|
131
91
|
|
|
132
92
|
```tsx
|
|
133
|
-
|
|
134
|
-
const [metrics, activity] = await Promise.all([getMetrics(), getRecentActivity()]);
|
|
135
|
-
return <><MetricsPanel data={metrics} /><ActivityFeed items={activity} /></>;
|
|
136
|
-
}
|
|
93
|
+
const [metrics, activity] = await Promise.all([getMetrics(), getRecentActivity()]);
|
|
137
94
|
```
|
|
138
95
|
|
|
139
|
-
## Caching
|
|
96
|
+
## Caching
|
|
140
97
|
|
|
141
98
|
| Mechanism | Scope | How to Use |
|
|
142
99
|
|-----------|-------|------------|
|
|
143
100
|
| **Request Memoization** | Per-request | Automatic deduplication of identical `fetch` calls |
|
|
144
|
-
| **Data Cache** | Cross-request |
|
|
101
|
+
| **Data Cache** | Cross-request | Cached by default; opt out with `cache: 'no-store'` |
|
|
145
102
|
| **Full Route Cache** | Build time | Static routes cached as HTML + RSC payload |
|
|
146
103
|
| **Router Cache** | Client-side | Prefetched and visited routes cached in browser |
|
|
147
104
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
// Time-based — revalidate every 60 seconds
|
|
152
|
-
fetch(url, { next: { revalidate: 60 } });
|
|
153
|
-
|
|
154
|
-
// On-demand — revalidate by path or tag
|
|
155
|
-
import { revalidatePath, revalidateTag } from 'next/cache';
|
|
156
|
-
revalidatePath('/blog');
|
|
157
|
-
revalidateTag('posts');
|
|
158
|
-
|
|
159
|
-
// Tag a fetch for on-demand revalidation
|
|
160
|
-
fetch(url, { next: { tags: ['posts'] } });
|
|
161
|
-
```
|
|
105
|
+
On-demand revalidation: `revalidatePath('/blog')` or `revalidateTag('posts')`. Tag a fetch: `fetch(url, { next: { tags: ['posts'] } })`.
|
|
162
106
|
|
|
163
107
|
## Routing
|
|
164
108
|
|
|
@@ -166,211 +110,61 @@ fetch(url, { next: { tags: ['posts'] } });
|
|
|
166
110
|
|
|
167
111
|
| File | Purpose |
|
|
168
112
|
|------|---------|
|
|
169
|
-
| `page.tsx` | Route UI
|
|
170
|
-
| `layout.tsx` | Shared layout (
|
|
113
|
+
| `page.tsx` | Route UI |
|
|
114
|
+
| `layout.tsx` | Shared layout (persists across navigation) |
|
|
171
115
|
| `template.tsx` | Like layout but re-mounts on navigation |
|
|
172
116
|
| `loading.tsx` | Loading UI (automatic Suspense boundary) |
|
|
173
|
-
| `error.tsx` | Error UI (Client Component
|
|
117
|
+
| `error.tsx` | Error UI (Client Component) |
|
|
174
118
|
| `not-found.tsx` | 404 UI |
|
|
175
|
-
| `route.ts` | API endpoint
|
|
119
|
+
| `route.ts` | API endpoint |
|
|
176
120
|
| `default.tsx` | Fallback for parallel routes |
|
|
177
121
|
|
|
178
122
|
### Dynamic Routes
|
|
179
123
|
|
|
180
|
-
|
|
181
|
-
app/
|
|
182
|
-
app/shop/[...slug]/page.tsx
|
|
183
|
-
app/shop/[[...slug]]/page.tsx → /shop or /shop/:slug+ (optional catch-all)
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Route Handlers (API Routes)
|
|
187
|
-
|
|
188
|
-
```ts
|
|
189
|
-
// app/api/users/route.ts
|
|
190
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
191
|
-
|
|
192
|
-
export async function GET(request: NextRequest) {
|
|
193
|
-
const { searchParams } = request.nextUrl;
|
|
194
|
-
const query = searchParams.get('q');
|
|
195
|
-
const users = await findUsers(query);
|
|
196
|
-
return NextResponse.json(users);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export async function POST(request: NextRequest) {
|
|
200
|
-
const body = await request.json();
|
|
201
|
-
const user = await createUser(body);
|
|
202
|
-
return NextResponse.json(user, { status: 201 });
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Error Handling
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
// app/dashboard/error.tsx — must be a Client Component
|
|
210
|
-
'use client';
|
|
211
|
-
export default function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
|
|
212
|
-
return <div role="alert"><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>;
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
// app/projects/[id]/page.tsx — trigger not-found boundary
|
|
218
|
-
import { notFound } from 'next/navigation';
|
|
219
|
-
export default async function ProjectPage({ params }: { params: { id: string } }) {
|
|
220
|
-
const project = await getProject(params.id);
|
|
221
|
-
if (!project) notFound();
|
|
222
|
-
return <h1>{project.name}</h1>;
|
|
223
|
-
}
|
|
224
|
-
```
|
|
124
|
+
- `app/blog/[slug]/page.tsx` → `/blog/:slug`
|
|
125
|
+
- `app/shop/[...slug]/page.tsx` → `/shop/:slug+` (catch-all)
|
|
126
|
+
- `app/shop/[[...slug]]/page.tsx` → `/shop` or `/shop/:slug+` (optional)
|
|
225
127
|
|
|
226
128
|
## Middleware
|
|
227
129
|
|
|
228
|
-
Runs at the Edge before every matched request. Use for auth, redirects, rewrites
|
|
130
|
+
Runs at the Edge before every matched request. Use for auth, redirects, rewrites.
|
|
229
131
|
|
|
230
132
|
```ts
|
|
231
133
|
import { NextResponse, type NextRequest } from 'next/server';
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return NextResponse.redirect(new URL('/login', request.url));
|
|
237
|
-
}
|
|
238
|
-
// Add custom headers
|
|
239
|
-
const response = NextResponse.next();
|
|
240
|
-
response.headers.set('x-request-id', crypto.randomUUID());
|
|
241
|
-
return response;
|
|
134
|
+
export function middleware(req: NextRequest) {
|
|
135
|
+
if (!req.cookies.get('session') && req.nextUrl.pathname.startsWith('/dashboard'))
|
|
136
|
+
return NextResponse.redirect(new URL('/login', req.url));
|
|
137
|
+
return NextResponse.next();
|
|
242
138
|
}
|
|
243
|
-
|
|
244
|
-
export const config = { matcher: ['/dashboard/:path*', '/settings/:path*'] };
|
|
139
|
+
export const config = { matcher: ['/dashboard/:path*'] };
|
|
245
140
|
```
|
|
246
141
|
|
|
247
|
-
##
|
|
248
|
-
|
|
249
|
-
### `next.config.ts`
|
|
250
|
-
|
|
251
|
-
```ts
|
|
252
|
-
import type { NextConfig } from 'next';
|
|
253
|
-
|
|
254
|
-
const nextConfig: NextConfig = {
|
|
255
|
-
reactStrictMode: true,
|
|
256
|
-
images: {
|
|
257
|
-
remotePatterns: [
|
|
258
|
-
{ protocol: 'https', hostname: 'cdn.example.com' },
|
|
259
|
-
],
|
|
260
|
-
},
|
|
261
|
-
experimental: {
|
|
262
|
-
ppr: true, // Partial Prerendering
|
|
263
|
-
typedRoutes: true, // Type-safe <Link> hrefs
|
|
264
|
-
},
|
|
265
|
-
// Redirects, rewrites, headers
|
|
266
|
-
async redirects() {
|
|
267
|
-
return [{ source: '/old-path', destination: '/new-path', permanent: true }];
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
export default nextConfig;
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### Environment Variables
|
|
142
|
+
## Environment Variables
|
|
275
143
|
|
|
276
144
|
| Prefix | Available in | Use Case |
|
|
277
145
|
|--------|-------------|----------|
|
|
278
146
|
| `NEXT_PUBLIC_` | Server + Client | Public values (API base URLs, feature flags) |
|
|
279
147
|
| No prefix | Server only | Secrets (DB URLs, API keys, tokens) |
|
|
280
148
|
|
|
281
|
-
Files
|
|
282
|
-
|
|
283
|
-
## Image and Font Optimization
|
|
284
|
-
|
|
285
|
-
### Images
|
|
286
|
-
|
|
287
|
-
```tsx
|
|
288
|
-
import Image from 'next/image';
|
|
289
|
-
import heroImg from '@/public/hero.jpg';
|
|
290
|
-
|
|
291
|
-
// Local image — auto width/height from import
|
|
292
|
-
<Image src={heroImg} alt="Hero" priority />
|
|
293
|
-
|
|
294
|
-
// Remote image — must specify dimensions
|
|
295
|
-
<Image src="https://cdn.example.com/photo.jpg" alt="Photo" width={800} height={600} />
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
### Fonts
|
|
299
|
-
|
|
300
|
-
```tsx
|
|
301
|
-
// app/layout.tsx
|
|
302
|
-
import { Inter } from 'next/font/google';
|
|
303
|
-
const inter = Inter({ subsets: ['latin'], display: 'swap' });
|
|
304
|
-
|
|
305
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
306
|
-
return <html lang="en" className={inter.className}><body>{children}</body></html>;
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
## Metadata and SEO
|
|
311
|
-
|
|
312
|
-
```tsx
|
|
313
|
-
// app/layout.tsx — static metadata
|
|
314
|
-
import type { Metadata } from 'next';
|
|
315
|
-
|
|
316
|
-
export const metadata: Metadata = {
|
|
317
|
-
title: { default: 'My App', template: '%s | My App' },
|
|
318
|
-
description: 'App description',
|
|
319
|
-
openGraph: { title: 'My App', description: 'App description', type: 'website' },
|
|
320
|
-
};
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
```tsx
|
|
324
|
-
// app/blog/[slug]/page.tsx — dynamic metadata
|
|
325
|
-
import type { Metadata } from 'next';
|
|
326
|
-
|
|
327
|
-
export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
|
|
328
|
-
const post = await getPost(params.slug);
|
|
329
|
-
return { title: post.title, description: post.excerpt };
|
|
330
|
-
}
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## Performance Patterns
|
|
334
|
-
|
|
335
|
-
- **Default to Server Components** — smaller client bundles, faster paint.
|
|
336
|
-
- **`<Suspense>` + `loading.tsx`** — stream content progressively; never block the whole page.
|
|
337
|
-
- **`Promise.all()`** — parallel data fetching for independent data.
|
|
338
|
-
- **Dynamic imports** — lazy-load heavy Client Components with `next/dynamic`.
|
|
339
|
-
- **`<Image>`** — automatic lazy loading, responsive sizing, format conversion.
|
|
340
|
-
- **`next/font`** — zero layout shift, self-hosted fonts.
|
|
341
|
-
- **Route segment config** — fine-tune caching and rendering per route.
|
|
342
|
-
|
|
343
|
-
## Deployment
|
|
344
|
-
|
|
345
|
-
Next.js deploys to multiple targets:
|
|
346
|
-
|
|
347
|
-
| Target | Config | Notes |
|
|
348
|
-
|--------|--------|-------|
|
|
349
|
-
| **Vercel** | Zero-config | Full feature support including Edge, ISR, Middleware |
|
|
350
|
-
| **Node.js server** | `output: 'standalone'` | Minimal `standalone/` folder with server |
|
|
351
|
-
| **Docker** | `output: 'standalone'` | Copy `.next/standalone` + `.next/static` + `public` |
|
|
352
|
-
| **Static export** | `output: 'export'` | No server features (no SSR, API routes, middleware) |
|
|
149
|
+
Files (priority order): `.env.local`, `.env.development` / `.env.production`, `.env`.
|
|
353
150
|
|
|
354
|
-
##
|
|
151
|
+
## Image, Font, and Metadata
|
|
355
152
|
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
-
|
|
359
|
-
- Co-locate tests with components.
|
|
360
|
-
- Folders: `kebab-case`. Types/Interfaces: `PascalCase`. Constants: `UPPER_SNAKE_CASE`.
|
|
153
|
+
- **Images**: Use `<Image>` from `next/image` — auto lazy loading, responsive sizing, format conversion. Configure `images.remotePatterns` in `next.config.ts` for remote URLs.
|
|
154
|
+
- **Fonts**: Use `next/font/google` — zero layout shift, self-hosted, no external network request.
|
|
155
|
+
- **Metadata**: Export `metadata` (static) or `generateMetadata` (dynamic) per page/layout for SEO and social previews.
|
|
361
156
|
|
|
362
157
|
## Anti-Patterns
|
|
363
158
|
|
|
364
159
|
| Anti-Pattern | Why It's Wrong | Do This Instead |
|
|
365
160
|
|-------------|---------------|-----------------|
|
|
366
|
-
| `'use client'` on every component | Bloats JS bundle, defeats RSC benefits | Default to Server Components
|
|
367
|
-
| Sequential `await` for independent data |
|
|
368
|
-
| `next/dynamic` with `ssr: false` in Server Components | Build/runtime crash | Extract to
|
|
369
|
-
| Fetching in `useEffect` when server fetch works | Extra
|
|
370
|
-
| Giant `layout.tsx` with all providers | Hard to test, couples
|
|
371
|
-
|
|
|
372
|
-
| Hardcoding secrets in source
|
|
161
|
+
| `'use client'` on every component | Bloats JS bundle, defeats RSC benefits | Default to Server Components |
|
|
162
|
+
| Sequential `await` for independent data | Waterfall, slows page load | Use `Promise.all()` |
|
|
163
|
+
| `next/dynamic` with `ssr: false` in Server Components | Build/runtime crash | Extract to Client Component |
|
|
164
|
+
| Fetching in `useEffect` when server fetch works | Extra roundtrip, loading flash | Fetch in Server Component or Server Actions |
|
|
165
|
+
| Giant `layout.tsx` with all providers | Hard to test, couples concerns | Split into a `Providers` Client Component |
|
|
166
|
+
| No `error.tsx` per segment | Unhandled errors crash the page | Add `error.tsx` per route segment |
|
|
167
|
+
| Hardcoding secrets in source | Security risk, version control leak | Use `.env.local` and `process.env` |
|
|
373
168
|
| Skipping `loading.tsx` / `<Suspense>` | Blank screen while data loads | Add `loading.tsx` or wrap in `<Suspense>` |
|
|
374
|
-
|
|
|
375
|
-
| Ignoring `next.config.ts` for images | Remote images blocked by default | Configure `images.remotePatterns` |
|
|
169
|
+
| `getServerSideProps` / `getStaticProps` | Legacy Pages Router | Use App Router with async Server Components |
|
|
376
170
|
| Missing `metadata` exports | Poor SEO, no social previews | Export `metadata` or `generateMetadata` per page |
|
|
@@ -96,98 +96,46 @@ The NX MCP server provides tools for understanding and working with the workspac
|
|
|
96
96
|
|
|
97
97
|
## Code Generation Workflow
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
Always prefer generators over manual file creation when a generator exists.
|
|
100
100
|
|
|
101
101
|
### Phase 1: Discover
|
|
102
102
|
|
|
103
|
-
1.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
3. **Prefer local generators** — when both local and plugin generators could work, **always prefer local** (they're customized for this repo's patterns)
|
|
107
|
-
4. **If no generator fits** — check `nx_available_plugins` for installable plugins. Only fall back to manual creation after exhausting all generator options
|
|
103
|
+
1. List available generators using `nx_generators` MCP tool (plugin + local workspace generators)
|
|
104
|
+
2. Prefer local generators over plugin generators — they're customized for this repo
|
|
105
|
+
3. If no generator fits, check `nx_available_plugins`; only fall back to manual creation after exhausting all generator options
|
|
108
106
|
|
|
109
107
|
### Phase 2: Understand
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- Identify required vs optional options
|
|
115
|
-
- Note default values that may need overriding
|
|
116
|
-
- Pay attention to options that affect file structure or naming
|
|
117
|
-
|
|
118
|
-
2. **Read generator source code** (for unfamiliar generators)
|
|
119
|
-
- Find source: `node -e "console.log(require.resolve('@nx/<plugin>/generators.json'));"`
|
|
120
|
-
- If that fails: read from `node_modules/<plugin>/generators.json`
|
|
121
|
-
- Local generators: check `tools/generators/` or local plugin directories
|
|
122
|
-
- Understanding the source reveals side effects (config updates, dep installs) and files created/modified
|
|
123
|
-
|
|
124
|
-
3. **Reevaluate generator choice** — after understanding what the generator does, confirm it's the right one. If not, go back to Phase 1 and select a different generator.
|
|
125
|
-
|
|
126
|
-
4. **Examine repo context** — study existing similar artifacts in the codebase:
|
|
127
|
-
- Look at how similar projects are structured (naming, test runner, build tool, linter)
|
|
128
|
-
- Match conventions when configuring the generator
|
|
129
|
-
- Note directory structures, file patterns, and config styles
|
|
130
|
-
|
|
131
|
-
5. **Validate required options** — map the user's request to generator options:
|
|
132
|
-
- Infer values from context where possible
|
|
133
|
-
- Ask for critical missing information if it cannot be inferred
|
|
109
|
+
1. Fetch generator schema using `nx_generator_schema` — note required options, defaults, and file structure impacts
|
|
110
|
+
2. Read generator source to understand side effects (config updates, dep installs, files created/modified)
|
|
111
|
+
3. Examine existing similar artifacts for naming, structure, test runner, and config conventions; map user's request to generator options
|
|
134
112
|
|
|
135
113
|
### Phase 3: Execute
|
|
136
114
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
- Shows files that would be created/deleted/modified (but not content)
|
|
142
|
-
- Some generators don't support dry-run (e.g., if they install packages) — skip and run for real
|
|
143
|
-
- For simple, well-understood generators, you may skip dry-run
|
|
115
|
+
```bash
|
|
116
|
+
yarn nx generate <generator-name> <options> --dry-run --no-interactive # optional preview
|
|
117
|
+
yarn nx generate <generator-name> <options> --no-interactive
|
|
118
|
+
```
|
|
144
119
|
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
yarn nx generate <generator-name> <options> --no-interactive
|
|
148
|
-
```
|
|
149
|
-
**CRITICAL**: Always include `--no-interactive` to prevent prompts that hang execution.
|
|
120
|
+
**CRITICAL**: Always include `--no-interactive` to prevent prompts that hang execution.
|
|
150
121
|
|
|
151
|
-
|
|
122
|
+
**CRITICAL**: Verify cwd before running — generators may use it to determine file placement.
|
|
152
123
|
|
|
153
|
-
|
|
154
|
-
- Read the error message carefully
|
|
155
|
-
- Common causes: missing required options, invalid values, conflicting files, missing dependencies
|
|
156
|
-
- Adjust options and retry
|
|
157
|
-
- Use the **self-improvement** skill to add a lesson if the fix was non-obvious
|
|
124
|
+
On failure: read the error, adjust options, and retry. Use the **self-improvement** skill for non-obvious fixes.
|
|
158
125
|
|
|
159
126
|
### Phase 4: Post-Generation
|
|
160
127
|
|
|
161
|
-
1.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- Integrate with existing code patterns
|
|
165
|
-
|
|
166
|
-
2. **Format code**:
|
|
167
|
-
```bash
|
|
168
|
-
yarn nx format --fix
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
3. **Run verification** on generated/affected projects:
|
|
172
|
-
```bash
|
|
173
|
-
yarn nx run <new-project>:lint --fix
|
|
174
|
-
yarn nx run <new-project>:test
|
|
175
|
-
yarn nx run <new-project>:build
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
4. **Handle verification failures**:
|
|
179
|
-
- **Small scope** (few lint errors, minor type issues) — fix directly, re-verify
|
|
180
|
-
- **Large scope** (many errors, complex problems) — fix obvious issues first, escalate remaining with description of what was generated, what's failing, and what was attempted
|
|
128
|
+
1. Modify generated code to match requirements (imports, exports, config, integrations)
|
|
129
|
+
2. Format: `yarn nx format --fix`
|
|
130
|
+
3. Verify lint, test, and build on generated/affected projects; fix small-scope issues directly, escalate large-scope failures
|
|
181
131
|
|
|
182
132
|
## Running Tasks Workflow
|
|
183
133
|
|
|
184
|
-
When helping with build, test, lint, or serve tasks:
|
|
185
|
-
|
|
186
134
|
1. Use `nx_current_running_tasks_details` to check for active/completed/failed tasks
|
|
187
|
-
2.
|
|
135
|
+
2. Use `nx_current_running_task_output` to get terminal output for a specific task
|
|
188
136
|
3. Diagnose issues from the output and apply fixes
|
|
189
|
-
4. To rerun
|
|
190
|
-
5. **Continuous tasks** (like `serve`) are already running — don't
|
|
137
|
+
4. To rerun, use `yarn nx run <taskId>` to preserve NX context
|
|
138
|
+
5. **Continuous tasks** (like `serve`) are already running — don't rerun, just check output
|
|
191
139
|
|
|
192
140
|
## Project Names
|
|
193
141
|
|
|
@@ -103,25 +103,12 @@ export class LoginPage {
|
|
|
103
103
|
}
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
## API Mocking
|
|
107
107
|
|
|
108
108
|
```typescript
|
|
109
|
-
// tests
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
type AuthFixtures = {
|
|
113
|
-
authenticatedPage: Page;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export const test = base.extend<AuthFixtures>({
|
|
117
|
-
authenticatedPage: async ({ page }, use) => {
|
|
118
|
-
await page.goto('/login');
|
|
119
|
-
await page.getByTestId('email-input').fill('test@example.com');
|
|
120
|
-
await page.getByTestId('password-input').fill('password');
|
|
121
|
-
await page.getByTestId('login-button').click();
|
|
122
|
-
await page.waitForURL('**/dashboard');
|
|
123
|
-
await use(page);
|
|
124
|
-
},
|
|
109
|
+
// Mock API responses for deterministic tests
|
|
110
|
+
await page.route('/api/login', (route) => {
|
|
111
|
+
route.fulfill({ status: 200, body: JSON.stringify({ token: 'test' }) });
|
|
125
112
|
});
|
|
126
113
|
```
|
|
127
114
|
|
|
@@ -189,3 +176,4 @@ The Playwright MCP server enables AI agents to interact with browsers directly:
|
|
|
189
176
|
- Run tests in parallel (`fullyParallel: true`) for speed
|
|
190
177
|
- Use `trace: 'on-first-retry'` to debug flaky tests
|
|
191
178
|
- Use `codegen` to bootstrap tests, then refactor into page objects
|
|
179
|
+
- Use `page.route()` to mock API responses — create isolated, deterministic tests without backend dependencies
|