ginskill-init 1.0.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/README.md +77 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +226 -0
- package/package.json +20 -0
- package/skills/ai-asset-generator/SKILL.md +255 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/generate-credit-assets.mjs +180 -0
- package/skills/ai-asset-generator/generate-ginbrowser-assets.mjs +242 -0
- package/skills/ai-asset-generator/generate-sty-icon.mjs +149 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +38 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +88 -0
- package/skills/ai-asset-generator/scripts/scaffold-generator.mjs +203 -0
- package/skills/ai-build-ai/SKILL.md +124 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/scripts/load-tutorial.sh +54 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +321 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/review-code/scripts/check-module.sh +201 -0
- package/skills/review-code/scripts/deep-scan.sh +604 -0
- package/skills/review-code/scripts/dep-check.sh +522 -0
- package/skills/review-code/scripts/detect-duplicates.sh +466 -0
- package/skills/review-code/scripts/format-check.sh +577 -0
- package/skills/review-code/scripts/run-review.sh +167 -0
- package/skills/review-code/scripts/scan-codebase.sh +152 -0
- package/skills/security-scanner/SKILL.md +327 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/security-scanner/scripts/security-scan.sh +478 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-fsd-architecture
|
|
3
|
+
description: |
|
|
4
|
+
**Feature-Sliced Design (FSD) Architecture**: Architectural methodology for organizing frontend projects by business features — layers, slices, segments, public APIs, and import rules.
|
|
5
|
+
- MANDATORY TRIGGERS: feature sliced design, FSD, feature slice, project structure, folder structure, architecture, organize code, layers slices segments, app structure, frontend architecture, scalable structure, code organization, module structure, refactor structure
|
|
6
|
+
- Use this skill whenever the user is setting up, refactoring, or discussing frontend project architecture and folder structure. Also trigger when deciding where to place new code, resolving circular dependencies, or organizing a React/React Native/Next.js project.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Feature-Sliced Design (FSD)
|
|
10
|
+
|
|
11
|
+
An architectural methodology for organizing frontend projects around **business features** instead of technical layers. Works with React, React Native, Next.js, Vue, Svelte — any frontend framework.
|
|
12
|
+
|
|
13
|
+
Official docs: https://feature-sliced.design
|
|
14
|
+
|
|
15
|
+
## The Golden Rule
|
|
16
|
+
|
|
17
|
+
> **A module in a slice can only import from slices on layers strictly below.**
|
|
18
|
+
|
|
19
|
+
This single rule prevents circular dependencies, enforces clear boundaries, and makes your codebase predictable.
|
|
20
|
+
|
|
21
|
+
## The Three Levels
|
|
22
|
+
|
|
23
|
+
FSD organizes code into 3 hierarchical levels:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Layer → Slice → Segment
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **Layer**: Why the code exists (its responsibility level)
|
|
30
|
+
- **Slice**: What business domain it belongs to
|
|
31
|
+
- **Segment**: How it's technically organized
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Layers (7 levels, top to bottom)
|
|
36
|
+
|
|
37
|
+
Layers are ordered by responsibility. Higher layers can import from lower layers, never the reverse.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
src/
|
|
41
|
+
├── app/ ← 1. App (highest — bootstrap, routing, providers)
|
|
42
|
+
├── processes/ ← 2. Processes (DEPRECATED — move to features or app)
|
|
43
|
+
├── pages/ ← 3. Pages (route-level screens)
|
|
44
|
+
├── widgets/ ← 4. Widgets (composite UI blocks)
|
|
45
|
+
├── features/ ← 5. Features (user interactions with business logic)
|
|
46
|
+
├── entities/ ← 6. Entities (business domain models)
|
|
47
|
+
└── shared/ ← 7. Shared (lowest — utilities, UI primitives, no business logic)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Layer Details
|
|
51
|
+
|
|
52
|
+
| Layer | Purpose | Contains | Has slices? |
|
|
53
|
+
|-------|---------|----------|-------------|
|
|
54
|
+
| **app** | Application bootstrap | Routing, providers, global styles, entry point | No — segments directly |
|
|
55
|
+
| **pages** | Route screens | Page UI, data loading, error boundaries | Yes — one slice per page |
|
|
56
|
+
| **widgets** | Composite UI blocks | Large self-sufficient sections reused across pages | Yes |
|
|
57
|
+
| **features** | User interactions | Business logic actions (login, add-to-cart, search) | Yes |
|
|
58
|
+
| **entities** | Domain models | Data models, schemas, API functions, entity UI | Yes — one slice per entity |
|
|
59
|
+
| **shared** | Foundation | UI kit, API client, utils, types, config, i18n | No — segments directly |
|
|
60
|
+
|
|
61
|
+
### Import Direction
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
app → pages → widgets → features → entities → shared
|
|
65
|
+
↓ ↓ ↓ ↓ ↓ ↓
|
|
66
|
+
Can import anything below. Cannot import from above or same layer.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Exceptions:**
|
|
70
|
+
- `app` and `shared` don't have slices — segments can reference each other freely within the layer
|
|
71
|
+
- `entities` can use `@x` cross-imports for entity relationships (see below)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Slices
|
|
76
|
+
|
|
77
|
+
Slices group code by **business meaning**. Their names reflect your domain, not technical concepts.
|
|
78
|
+
|
|
79
|
+
### Good slice names (domain-driven)
|
|
80
|
+
```
|
|
81
|
+
entities/user/
|
|
82
|
+
entities/product/
|
|
83
|
+
entities/order/
|
|
84
|
+
features/authentication/
|
|
85
|
+
features/add-to-cart/
|
|
86
|
+
features/search/
|
|
87
|
+
pages/home/
|
|
88
|
+
pages/product-detail/
|
|
89
|
+
widgets/header/
|
|
90
|
+
widgets/product-card/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Bad slice names (tech-driven — avoid)
|
|
94
|
+
```
|
|
95
|
+
features/hooks/ ← this is a segment name, not a slice
|
|
96
|
+
features/utils/ ← belongs in shared/lib
|
|
97
|
+
entities/api/ ← API is a segment, not an entity
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Slice Rules
|
|
101
|
+
|
|
102
|
+
1. **Zero coupling** between slices on the same layer — they cannot import from each other
|
|
103
|
+
2. **High cohesion** — everything related to one concept stays in one slice
|
|
104
|
+
3. **Public API required** — each slice exposes an `index.ts`, external code imports only from this file
|
|
105
|
+
4. **Domain naming** — names reflect what the user/business cares about
|
|
106
|
+
|
|
107
|
+
### Slice Groups
|
|
108
|
+
|
|
109
|
+
Related slices can be grouped in folders for organization, but the group folder itself cannot contain shared code:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
entities/
|
|
113
|
+
├── user/ # slice
|
|
114
|
+
├── product/ # slice
|
|
115
|
+
├── @catalog/ # group folder (just for organization)
|
|
116
|
+
│ ├── category/ # slice
|
|
117
|
+
│ └── brand/ # slice
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Segments
|
|
123
|
+
|
|
124
|
+
Segments are the lowest level — they group code by **technical purpose** within a slice.
|
|
125
|
+
|
|
126
|
+
### Standard Segment Names
|
|
127
|
+
|
|
128
|
+
| Segment | Contains | Example files |
|
|
129
|
+
|---------|----------|---------------|
|
|
130
|
+
| `ui` | Components, styles, formatters | `ProductCard.tsx`, `ProductCard.styles.ts` |
|
|
131
|
+
| `model` | State, business logic, types, hooks | `useProduct.ts`, `product.store.ts`, `product.types.ts` |
|
|
132
|
+
| `api` | Backend interactions, request functions | `product.api.ts`, `product.dto.ts` |
|
|
133
|
+
| `lib` | Internal utilities for this slice | `formatPrice.ts`, `validateSku.ts` |
|
|
134
|
+
| `config` | Constants, feature flags | `product.config.ts` |
|
|
135
|
+
|
|
136
|
+
**Additional custom segments** are allowed (especially in `app` and `shared`):
|
|
137
|
+
- `i18n` — translations
|
|
138
|
+
- `router` — routing config
|
|
139
|
+
- `assets` — images, icons for this slice
|
|
140
|
+
|
|
141
|
+
### Not every slice needs all segments
|
|
142
|
+
|
|
143
|
+
Start with `ui` and `model`. Add `api`, `lib`, `config` only when needed:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
features/
|
|
147
|
+
├── authentication/
|
|
148
|
+
│ ├── ui/
|
|
149
|
+
│ │ └── LoginForm.tsx
|
|
150
|
+
│ ├── model/
|
|
151
|
+
│ │ ├── auth.store.ts
|
|
152
|
+
│ │ └── auth.types.ts
|
|
153
|
+
│ ├── api/
|
|
154
|
+
│ │ └── auth.api.ts
|
|
155
|
+
│ └── index.ts ← public API
|
|
156
|
+
│
|
|
157
|
+
├── search/
|
|
158
|
+
│ ├── ui/
|
|
159
|
+
│ │ └── SearchBar.tsx
|
|
160
|
+
│ ├── model/
|
|
161
|
+
│ │ └── useSearch.ts
|
|
162
|
+
│ └── index.ts ← public API (no api/ or lib/ needed)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Public API (index.ts)
|
|
168
|
+
|
|
169
|
+
Every slice **must** have an `index.ts` that explicitly re-exports what's public.
|
|
170
|
+
|
|
171
|
+
### Rules
|
|
172
|
+
|
|
173
|
+
1. **Explicit named exports** — never use `export *`
|
|
174
|
+
2. **Only export what consumers need** — components, types, hooks, constants
|
|
175
|
+
3. **Keep internals private** — helper functions, internal components, implementation details stay unexported
|
|
176
|
+
4. **External code must import from the index** — never deep-import into a slice
|
|
177
|
+
|
|
178
|
+
### Pattern
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// features/authentication/index.ts
|
|
182
|
+
|
|
183
|
+
// UI
|
|
184
|
+
export { LoginForm } from './ui/LoginForm'
|
|
185
|
+
export { SignUpForm } from './ui/SignUpForm'
|
|
186
|
+
|
|
187
|
+
// Model (hooks, types)
|
|
188
|
+
export { useAuth } from './model/auth.store'
|
|
189
|
+
export type { User, AuthState } from './model/auth.types'
|
|
190
|
+
|
|
191
|
+
// API (if consumers need direct access)
|
|
192
|
+
export { loginApi, registerApi } from './api/auth.api'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Import Rules
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// GOOD — import from public API
|
|
199
|
+
import { LoginForm, useAuth } from '@/features/authentication'
|
|
200
|
+
|
|
201
|
+
// BAD — deep import into slice internals
|
|
202
|
+
import { LoginForm } from '@/features/authentication/ui/LoginForm'
|
|
203
|
+
import { validateEmail } from '@/features/authentication/lib/validation'
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Shared Layer Public API
|
|
207
|
+
|
|
208
|
+
Since `shared` has no slices, each **segment** gets its own public API:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// shared/ui/index.ts
|
|
212
|
+
export { Button } from './Button'
|
|
213
|
+
export { Input } from './Input'
|
|
214
|
+
export { Modal } from './Modal'
|
|
215
|
+
|
|
216
|
+
// shared/api/index.ts
|
|
217
|
+
export { apiClient } from './client'
|
|
218
|
+
export type { ApiError } from './types'
|
|
219
|
+
|
|
220
|
+
// shared/lib/index.ts
|
|
221
|
+
export { formatDate, formatCurrency } from './formatters'
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Consumer imports by segment
|
|
226
|
+
import { Button, Input } from '@/shared/ui'
|
|
227
|
+
import { apiClient } from '@/shared/api'
|
|
228
|
+
import { formatDate } from '@/shared/lib'
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Cross-Imports (@x Notation)
|
|
234
|
+
|
|
235
|
+
Slices on the same layer normally cannot import from each other. The one exception is **entities** that have relationships.
|
|
236
|
+
|
|
237
|
+
### When to use
|
|
238
|
+
|
|
239
|
+
When entity B's data contains entity A's data (e.g., Order contains Product), use the `@x` notation:
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
entities/
|
|
243
|
+
├── product/
|
|
244
|
+
│ ├── @x/
|
|
245
|
+
│ │ └── order.ts ← public API specifically for the order entity
|
|
246
|
+
│ ├── ui/
|
|
247
|
+
│ ├── model/
|
|
248
|
+
│ └── index.ts
|
|
249
|
+
├── order/
|
|
250
|
+
│ ├── ui/
|
|
251
|
+
│ │ └── OrderCard.tsx ← can import from entities/product/@x/order
|
|
252
|
+
│ ├── model/
|
|
253
|
+
│ └── index.ts
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// entities/product/@x/order.ts
|
|
258
|
+
// Only export what the order entity needs
|
|
259
|
+
export { ProductPreview } from '../ui/ProductPreview'
|
|
260
|
+
export type { ProductSummary } from '../model/product.types'
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Rules for @x
|
|
264
|
+
|
|
265
|
+
- Only use on the **entities** layer
|
|
266
|
+
- Keep cross-imports **minimal** — only what's truly needed
|
|
267
|
+
- All layers above entities still have cross-imports **forbidden**
|
|
268
|
+
- `@x` files are explicit — they document the coupling between entities
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Path Aliases
|
|
273
|
+
|
|
274
|
+
Set up TypeScript path aliases to match the layers:
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
// tsconfig.json
|
|
278
|
+
{
|
|
279
|
+
"compilerOptions": {
|
|
280
|
+
"paths": {
|
|
281
|
+
"@/*": ["./src/*"],
|
|
282
|
+
"@/app/*": ["./src/app/*"],
|
|
283
|
+
"@/pages/*": ["./src/pages/*"],
|
|
284
|
+
"@/widgets/*": ["./src/widgets/*"],
|
|
285
|
+
"@/features/*": ["./src/features/*"],
|
|
286
|
+
"@/entities/*": ["./src/entities/*"],
|
|
287
|
+
"@/shared/*": ["./src/shared/*"]
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Framework-Specific Patterns
|
|
296
|
+
|
|
297
|
+
### Next.js (App Router)
|
|
298
|
+
|
|
299
|
+
Next.js App Router has its own `app/` directory for routes. Combine with FSD:
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
src/
|
|
303
|
+
├── app/ ← Next.js App Router (routing + layouts)
|
|
304
|
+
│ ├── layout.tsx
|
|
305
|
+
│ ├── page.tsx → imports from @/pages/home
|
|
306
|
+
│ ├── products/
|
|
307
|
+
│ │ └── [id]/
|
|
308
|
+
│ │ └── page.tsx → imports from @/pages/product-detail
|
|
309
|
+
│ └── providers.tsx
|
|
310
|
+
├── pages/ ← FSD pages layer (page UI logic)
|
|
311
|
+
│ ├── home/
|
|
312
|
+
│ └── product-detail/
|
|
313
|
+
├── widgets/
|
|
314
|
+
├── features/
|
|
315
|
+
├── entities/
|
|
316
|
+
└── shared/
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
The Next.js `app/` directory handles routing. FSD `pages/` holds the actual page UI and logic. Next.js route files are thin wrappers:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// src/app/products/[id]/page.tsx
|
|
323
|
+
import { ProductDetailPage } from '@/pages/product-detail'
|
|
324
|
+
|
|
325
|
+
export default function Page({ params }: { params: { id: string } }) {
|
|
326
|
+
return <ProductDetailPage id={params.id} />
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### React Native (Expo Router)
|
|
331
|
+
|
|
332
|
+
Same principle — Expo Router's `app/` directory handles navigation, FSD handles architecture:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
src/
|
|
336
|
+
├── app/ ← Expo Router (file-based routing)
|
|
337
|
+
│ ├── (tabs)/
|
|
338
|
+
│ │ ├── index.tsx → imports from @/pages/home
|
|
339
|
+
│ │ ├── profile.tsx → imports from @/pages/profile
|
|
340
|
+
│ │ └── _layout.tsx
|
|
341
|
+
│ └── _layout.tsx
|
|
342
|
+
├── pages/ ← FSD pages layer
|
|
343
|
+
│ ├── home/
|
|
344
|
+
│ └── profile/
|
|
345
|
+
├── widgets/
|
|
346
|
+
├── features/
|
|
347
|
+
├── entities/
|
|
348
|
+
└── shared/
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Plain React (Vite / CRA)
|
|
352
|
+
|
|
353
|
+
FSD `app/` layer handles routing directly:
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
src/
|
|
357
|
+
├── app/
|
|
358
|
+
│ ├── App.tsx
|
|
359
|
+
│ ├── providers/
|
|
360
|
+
│ ├── router/
|
|
361
|
+
│ └── styles/
|
|
362
|
+
├── pages/
|
|
363
|
+
├── widgets/
|
|
364
|
+
├── features/
|
|
365
|
+
├── entities/
|
|
366
|
+
└── shared/
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Complete Example
|
|
372
|
+
|
|
373
|
+
A todo app structured with FSD:
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
src/
|
|
377
|
+
├── app/
|
|
378
|
+
│ ├── providers/
|
|
379
|
+
│ │ ├── QueryProvider.tsx
|
|
380
|
+
│ │ └── ThemeProvider.tsx
|
|
381
|
+
│ ├── router/
|
|
382
|
+
│ │ └── index.tsx
|
|
383
|
+
│ ├── styles/
|
|
384
|
+
│ │ └── global.css
|
|
385
|
+
│ └── index.tsx
|
|
386
|
+
│
|
|
387
|
+
├── pages/
|
|
388
|
+
│ ├── home/
|
|
389
|
+
│ │ ├── ui/
|
|
390
|
+
│ │ │ └── HomePage.tsx
|
|
391
|
+
│ │ └── index.ts
|
|
392
|
+
│ └── settings/
|
|
393
|
+
│ ├── ui/
|
|
394
|
+
│ │ └── SettingsPage.tsx
|
|
395
|
+
│ └── index.ts
|
|
396
|
+
│
|
|
397
|
+
├── widgets/
|
|
398
|
+
│ ├── todo-list/
|
|
399
|
+
│ │ ├── ui/
|
|
400
|
+
│ │ │ └── TodoList.tsx
|
|
401
|
+
│ │ └── index.ts
|
|
402
|
+
│ └── header/
|
|
403
|
+
│ ├── ui/
|
|
404
|
+
│ │ └── Header.tsx
|
|
405
|
+
│ └── index.ts
|
|
406
|
+
│
|
|
407
|
+
├── features/
|
|
408
|
+
│ ├── create-todo/
|
|
409
|
+
│ │ ├── ui/
|
|
410
|
+
│ │ │ └── CreateTodoForm.tsx
|
|
411
|
+
│ │ ├── model/
|
|
412
|
+
│ │ │ └── useCreateTodo.ts
|
|
413
|
+
│ │ ├── api/
|
|
414
|
+
│ │ │ └── createTodo.api.ts
|
|
415
|
+
│ │ └── index.ts
|
|
416
|
+
│ ├── toggle-todo/
|
|
417
|
+
│ │ ├── ui/
|
|
418
|
+
│ │ │ └── ToggleButton.tsx
|
|
419
|
+
│ │ ├── model/
|
|
420
|
+
│ │ │ └── useToggleTodo.ts
|
|
421
|
+
│ │ └── index.ts
|
|
422
|
+
│ └── delete-todo/
|
|
423
|
+
│ ├── ui/
|
|
424
|
+
│ │ └── DeleteButton.tsx
|
|
425
|
+
│ ├── model/
|
|
426
|
+
│ │ └── useDeleteTodo.ts
|
|
427
|
+
│ └── index.ts
|
|
428
|
+
│
|
|
429
|
+
├── entities/
|
|
430
|
+
│ └── todo/
|
|
431
|
+
│ ├── ui/
|
|
432
|
+
│ │ ├── TodoCard.tsx
|
|
433
|
+
│ │ └── TodoStatus.tsx
|
|
434
|
+
│ ├── model/
|
|
435
|
+
│ │ ├── todo.types.ts
|
|
436
|
+
│ │ └── todo.store.ts
|
|
437
|
+
│ ├── api/
|
|
438
|
+
│ │ └── todo.api.ts
|
|
439
|
+
│ └── index.ts
|
|
440
|
+
│
|
|
441
|
+
└── shared/
|
|
442
|
+
├── ui/
|
|
443
|
+
│ ├── Button.tsx
|
|
444
|
+
│ ├── Input.tsx
|
|
445
|
+
│ ├── Modal.tsx
|
|
446
|
+
│ └── index.ts
|
|
447
|
+
├── api/
|
|
448
|
+
│ ├── client.ts
|
|
449
|
+
│ └── index.ts
|
|
450
|
+
├── lib/
|
|
451
|
+
│ ├── formatDate.ts
|
|
452
|
+
│ └── index.ts
|
|
453
|
+
├── config/
|
|
454
|
+
│ ├── env.ts
|
|
455
|
+
│ └── index.ts
|
|
456
|
+
└── types/
|
|
457
|
+
└── index.ts
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Real-World Example: Admin Dashboard
|
|
461
|
+
|
|
462
|
+
An admin dashboard with log management, login history, and client log ingestion — based on actual Styai/EasyCloset API endpoints.
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
src/
|
|
466
|
+
├── app/
|
|
467
|
+
│ ├── providers/
|
|
468
|
+
│ ├── router/
|
|
469
|
+
│ └── index.tsx
|
|
470
|
+
│
|
|
471
|
+
├── pages/
|
|
472
|
+
│ ├── @admin/ ← slice group for admin-only pages
|
|
473
|
+
│ │ ├── log-management/
|
|
474
|
+
│ │ │ ├── ui/
|
|
475
|
+
│ │ │ │ └── LogManagementPage.tsx
|
|
476
|
+
│ │ │ └── index.ts
|
|
477
|
+
│ │ └── dashboard/
|
|
478
|
+
│ │ ├── ui/
|
|
479
|
+
│ │ │ └── DashboardPage.tsx
|
|
480
|
+
│ │ └── index.ts
|
|
481
|
+
│ └── @user/ ← slice group for user-facing pages
|
|
482
|
+
│ └── login-history/
|
|
483
|
+
│ ├── ui/
|
|
484
|
+
│ │ └── LoginHistoryPage.tsx
|
|
485
|
+
│ └── index.ts
|
|
486
|
+
│
|
|
487
|
+
├── widgets/
|
|
488
|
+
│ └── log-viewer/ ← composes search + table + download
|
|
489
|
+
│ ├── ui/
|
|
490
|
+
│ │ └── LogViewer.tsx
|
|
491
|
+
│ └── index.ts
|
|
492
|
+
│
|
|
493
|
+
├── features/
|
|
494
|
+
│ ├── search-logs/ ← search form with filters
|
|
495
|
+
│ │ ├── ui/
|
|
496
|
+
│ │ │ └── LogSearchForm.tsx
|
|
497
|
+
│ │ ├── model/
|
|
498
|
+
│ │ │ └── useLogSearch.ts
|
|
499
|
+
│ │ └── index.ts
|
|
500
|
+
│ └── download-log-file/ ← file download action
|
|
501
|
+
│ ├── ui/
|
|
502
|
+
│ │ └── DownloadLogButton.tsx
|
|
503
|
+
│ ├── model/
|
|
504
|
+
│ │ └── useDownloadLog.ts
|
|
505
|
+
│ └── index.ts
|
|
506
|
+
│
|
|
507
|
+
├── entities/
|
|
508
|
+
│ ├── system-log/ ← GET /api/v1/system/logs/*
|
|
509
|
+
│ │ ├── ui/
|
|
510
|
+
│ │ │ ├── LogTable.tsx
|
|
511
|
+
│ │ │ └── LogLevelBadge.tsx
|
|
512
|
+
│ │ ├── model/
|
|
513
|
+
│ │ │ ├── system-log.types.ts
|
|
514
|
+
│ │ │ └── system-log.queries.ts
|
|
515
|
+
│ │ ├── api/
|
|
516
|
+
│ │ │ └── system-log.api.ts
|
|
517
|
+
│ │ └── index.ts
|
|
518
|
+
│ └── login-history/ ← GET /api/v1/user/login-history
|
|
519
|
+
│ ├── ui/
|
|
520
|
+
│ │ └── LoginHistoryTable.tsx
|
|
521
|
+
│ ├── model/
|
|
522
|
+
│ │ ├── login-history.types.ts
|
|
523
|
+
│ │ └── login-history.queries.ts
|
|
524
|
+
│ ├── api/
|
|
525
|
+
│ │ └── login-history.api.ts
|
|
526
|
+
│ └── index.ts
|
|
527
|
+
│
|
|
528
|
+
└── shared/
|
|
529
|
+
├── ui/
|
|
530
|
+
├── api/
|
|
531
|
+
│ └── client.ts
|
|
532
|
+
├── lib/
|
|
533
|
+
│ ├── client-logger.ts ← POST /api/v1/logs/ingest
|
|
534
|
+
│ └── index.ts
|
|
535
|
+
└── config/
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Key patterns:**
|
|
539
|
+
- `@admin/` and `@user/` slice groups organize pages by role without adding shared code
|
|
540
|
+
- `system-log` entity owns API types/hooks matching the backend response shape
|
|
541
|
+
- `client-logger` lives in `shared/lib` — it has no domain UI, just a fire-and-forget service
|
|
542
|
+
- The `log-viewer` widget composes `search-logs` + `system-log` table + `download-log-file`
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Common Mistakes & Anti-Patterns
|
|
547
|
+
|
|
548
|
+
### 1. Importing from a higher layer
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// BAD — entity importing from a feature (higher layer)
|
|
552
|
+
// entities/todo/model/todo.store.ts
|
|
553
|
+
import { useAuth } from '@/features/authentication' // ← VIOLATION
|
|
554
|
+
|
|
555
|
+
// GOOD — pass auth data as props/params from the page or widget
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### 2. Cross-importing slices on the same layer (except entities @x)
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// BAD — one feature importing another feature
|
|
562
|
+
import { useSearch } from '@/features/search' // inside features/filter/
|
|
563
|
+
|
|
564
|
+
// GOOD — compose in a widget or page that imports both features
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### 3. Putting everything in shared
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
// BAD — shared becomes a dumping ground
|
|
571
|
+
shared/
|
|
572
|
+
├── components/ ← too vague
|
|
573
|
+
├── helpers/ ← what kind of helpers?
|
|
574
|
+
├── hooks/ ← for what domain?
|
|
575
|
+
└── utils/ ← everything goes here
|
|
576
|
+
|
|
577
|
+
// GOOD — shared has clear segments, domain logic goes in entities/features
|
|
578
|
+
shared/
|
|
579
|
+
├── ui/ ← only generic UI primitives (Button, Input, Modal)
|
|
580
|
+
├── api/ ← only the API client
|
|
581
|
+
├── lib/ ← only pure utility functions
|
|
582
|
+
└── config/ ← only environment/app config
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### 4. Skipping the public API
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// BAD — deep importing into slice internals
|
|
589
|
+
import { validateEmail } from '@/features/authentication/lib/validation'
|
|
590
|
+
|
|
591
|
+
// GOOD — export through index.ts if it's meant to be public
|
|
592
|
+
import { validateEmail } from '@/features/authentication'
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### 5. Using `export *` in index.ts
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
// BAD — exposes internals, breaks encapsulation
|
|
599
|
+
export * from './ui'
|
|
600
|
+
export * from './model'
|
|
601
|
+
export * from './api'
|
|
602
|
+
|
|
603
|
+
// GOOD — explicit named exports
|
|
604
|
+
export { LoginForm } from './ui/LoginForm'
|
|
605
|
+
export { useAuth } from './model/auth.store'
|
|
606
|
+
export type { User } from './model/auth.types'
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### 6. Making features too granular or too broad
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// TOO GRANULAR — one feature per button
|
|
613
|
+
features/
|
|
614
|
+
├── click-login-button/
|
|
615
|
+
├── click-signup-button/
|
|
616
|
+
├── click-forgot-password/
|
|
617
|
+
|
|
618
|
+
// TOO BROAD — everything in one feature
|
|
619
|
+
features/
|
|
620
|
+
├── user-management/ ← contains login, signup, profile, settings...
|
|
621
|
+
|
|
622
|
+
// RIGHT — one feature per user interaction
|
|
623
|
+
features/
|
|
624
|
+
├── authentication/ ← login + signup + password reset
|
|
625
|
+
├── edit-profile/
|
|
626
|
+
├── manage-notifications/
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### 7. Confusing widgets with features
|
|
630
|
+
|
|
631
|
+
- **Feature** = an action/interaction (add-to-cart, search, toggle-theme)
|
|
632
|
+
- **Widget** = a UI composition (header, sidebar, product-card-with-actions)
|
|
633
|
+
|
|
634
|
+
Widgets **compose** features and entities. Features **contain** business logic.
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## Migration Strategy
|
|
639
|
+
|
|
640
|
+
### Incremental adoption (recommended)
|
|
641
|
+
|
|
642
|
+
1. **Start with `shared/`** — extract UI kit, API client, utils
|
|
643
|
+
2. **Create `entities/`** — identify your domain models (user, product, order)
|
|
644
|
+
3. **Create `features/`** — extract interactive behaviors from pages
|
|
645
|
+
4. **Create `pages/`** — thin page shells that compose widgets/features
|
|
646
|
+
5. **Add `widgets/` only when needed** — for reusable composite blocks
|
|
647
|
+
|
|
648
|
+
### Don't try to restructure everything at once
|
|
649
|
+
|
|
650
|
+
- Move one feature at a time
|
|
651
|
+
- Keep old and new structures working in parallel
|
|
652
|
+
- Use ESLint plugin (`eslint-plugin-boundaries` or `steiger`) to enforce rules gradually
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## Tooling
|
|
657
|
+
|
|
658
|
+
### Linting
|
|
659
|
+
|
|
660
|
+
Use [Steiger](https://github.com/feature-sliced/steiger) — the official FSD linter:
|
|
661
|
+
|
|
662
|
+
```bash
|
|
663
|
+
npx steiger src/
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Or use `eslint-plugin-boundaries` for import rule enforcement.
|
|
667
|
+
|
|
668
|
+
### ESLint Rule
|
|
669
|
+
|
|
670
|
+
Enforce the import direction rule:
|
|
671
|
+
|
|
672
|
+
```json
|
|
673
|
+
{
|
|
674
|
+
"rules": {
|
|
675
|
+
"boundaries/element-types": [2, {
|
|
676
|
+
"default": "disallow",
|
|
677
|
+
"rules": [
|
|
678
|
+
{ "from": "app", "allow": ["pages", "widgets", "features", "entities", "shared"] },
|
|
679
|
+
{ "from": "pages", "allow": ["widgets", "features", "entities", "shared"] },
|
|
680
|
+
{ "from": "widgets", "allow": ["features", "entities", "shared"] },
|
|
681
|
+
{ "from": "features", "allow": ["entities", "shared"] },
|
|
682
|
+
{ "from": "entities", "allow": ["shared"] },
|
|
683
|
+
{ "from": "shared", "allow": [] }
|
|
684
|
+
]
|
|
685
|
+
}]
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
## Further Reading
|
|
691
|
+
|
|
692
|
+
For detailed patterns and edge cases, see:
|
|
693
|
+
- `references/fsd-patterns.md` — Composition patterns, testing strategy, state management integration
|