@corbat-tech/coding-standards-mcp 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/LICENSE +21 -0
- package/README.md +371 -0
- package/assets/demo.gif +0 -0
- package/dist/agent.d.ts +53 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +629 -0
- package/dist/agent.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +651 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/config.d.ts +73 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +105 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/profiles.d.ts +39 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +526 -0
- package/dist/profiles.js.map +1 -0
- package/dist/prompts-legacy.d.ts +25 -0
- package/dist/prompts-legacy.d.ts.map +1 -0
- package/dist/prompts-legacy.js +600 -0
- package/dist/prompts-legacy.js.map +1 -0
- package/dist/prompts-v2.d.ts +30 -0
- package/dist/prompts-v2.d.ts.map +1 -0
- package/dist/prompts-v2.js +310 -0
- package/dist/prompts-v2.js.map +1 -0
- package/dist/prompts.d.ts +30 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +310 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +18 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +95 -0
- package/dist/resources.js.map +1 -0
- package/dist/tools-legacy.d.ts +196 -0
- package/dist/tools-legacy.d.ts.map +1 -0
- package/dist/tools-legacy.js +1230 -0
- package/dist/tools-legacy.js.map +1 -0
- package/dist/tools-v2.d.ts +92 -0
- package/dist/tools-v2.d.ts.map +1 -0
- package/dist/tools-v2.js +410 -0
- package/dist/tools-v2.js.map +1 -0
- package/dist/tools.d.ts +92 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +410 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +3054 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +515 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/retry.d.ts +44 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +74 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +79 -0
- package/profiles/README.md +199 -0
- package/profiles/custom/.gitkeep +2 -0
- package/profiles/templates/_template.yaml +159 -0
- package/profiles/templates/angular.yaml +494 -0
- package/profiles/templates/java-spring-backend.yaml +512 -0
- package/profiles/templates/minimal.yaml +102 -0
- package/profiles/templates/nodejs.yaml +338 -0
- package/profiles/templates/python.yaml +340 -0
- package/profiles/templates/react.yaml +331 -0
- package/profiles/templates/vue.yaml +598 -0
- package/standards/architecture/ddd.md +173 -0
- package/standards/architecture/hexagonal.md +97 -0
- package/standards/cicd/github-actions.md +567 -0
- package/standards/clean-code/naming.md +175 -0
- package/standards/clean-code/principles.md +179 -0
- package/standards/containerization/dockerfile.md +419 -0
- package/standards/database/selection-guide.md +443 -0
- package/standards/documentation/guidelines.md +189 -0
- package/standards/event-driven/domain-events.md +527 -0
- package/standards/kubernetes/deployment.md +518 -0
- package/standards/observability/guidelines.md +665 -0
- package/standards/project-setup/initialization-checklist.md +650 -0
- package/standards/spring-boot/best-practices.md +598 -0
- package/standards/testing/guidelines.md +559 -0
- package/standards/workflow/llm-development-workflow.md +542 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# CORBAT MCP - Angular Profile
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# Production-ready standards for Angular applications (v19+).
|
|
5
|
+
# Covers standalone components, signals, zoneless, new control flow, and modern patterns.
|
|
6
|
+
# Updated for Angular 19/20/21 best practices (2025-2026).
|
|
7
|
+
# ============================================================================
|
|
8
|
+
|
|
9
|
+
name: "Angular Standards"
|
|
10
|
+
description: "Production-ready standards for Angular 19+ applications with TypeScript, focusing on standalone components, signals, zoneless change detection, and modern Angular patterns"
|
|
11
|
+
|
|
12
|
+
# ----------------------------------------------------------------------------
|
|
13
|
+
# ARCHITECTURE
|
|
14
|
+
# ----------------------------------------------------------------------------
|
|
15
|
+
architecture:
|
|
16
|
+
type: "feature-based"
|
|
17
|
+
enforceLayerDependencies: true
|
|
18
|
+
layers:
|
|
19
|
+
- name: features
|
|
20
|
+
description: "Feature modules with standalone components, each feature is self-contained"
|
|
21
|
+
allowedDependencies:
|
|
22
|
+
- shared
|
|
23
|
+
- core
|
|
24
|
+
packages:
|
|
25
|
+
- "src/app/features/*"
|
|
26
|
+
|
|
27
|
+
- name: shared
|
|
28
|
+
description: "Reusable standalone components, directives, pipes used across features"
|
|
29
|
+
allowedDependencies:
|
|
30
|
+
- core
|
|
31
|
+
packages:
|
|
32
|
+
- "src/app/shared"
|
|
33
|
+
- "src/app/shared/components"
|
|
34
|
+
- "src/app/shared/directives"
|
|
35
|
+
- "src/app/shared/pipes"
|
|
36
|
+
|
|
37
|
+
- name: core
|
|
38
|
+
description: "Singleton services, guards, interceptors, models - provided in root"
|
|
39
|
+
allowedDependencies: []
|
|
40
|
+
packages:
|
|
41
|
+
- "src/app/core"
|
|
42
|
+
- "src/app/core/services"
|
|
43
|
+
- "src/app/core/guards"
|
|
44
|
+
- "src/app/core/interceptors"
|
|
45
|
+
- "src/app/core/models"
|
|
46
|
+
|
|
47
|
+
# ----------------------------------------------------------------------------
|
|
48
|
+
# CODE QUALITY
|
|
49
|
+
# ----------------------------------------------------------------------------
|
|
50
|
+
codeQuality:
|
|
51
|
+
maxMethodLines: 25
|
|
52
|
+
maxClassLines: 300
|
|
53
|
+
maxFileLines: 400
|
|
54
|
+
maxMethodParameters: 4
|
|
55
|
+
maxCyclomaticComplexity: 10
|
|
56
|
+
requireDocumentation: true
|
|
57
|
+
requireTests: true
|
|
58
|
+
minimumTestCoverage: 70
|
|
59
|
+
|
|
60
|
+
principles:
|
|
61
|
+
- "Single Responsibility - one component, one purpose"
|
|
62
|
+
- "Smart/Dumb components pattern (container/presentational)"
|
|
63
|
+
- "Prefer standalone components over NgModules"
|
|
64
|
+
- "Use signals for reactive state"
|
|
65
|
+
- "Prefer new control flow (@if, @for, @switch)"
|
|
66
|
+
- "OnPush change detection by default"
|
|
67
|
+
- "Inject function over constructor injection"
|
|
68
|
+
|
|
69
|
+
# ----------------------------------------------------------------------------
|
|
70
|
+
# NAMING CONVENTIONS
|
|
71
|
+
# ----------------------------------------------------------------------------
|
|
72
|
+
naming:
|
|
73
|
+
general:
|
|
74
|
+
component: "PascalCase + .component.ts suffix"
|
|
75
|
+
service: "PascalCase + .service.ts suffix"
|
|
76
|
+
directive: "camelCase selector + .directive.ts suffix"
|
|
77
|
+
pipe: "camelCase name + .pipe.ts suffix"
|
|
78
|
+
guard: "camelCase + .guard.ts suffix"
|
|
79
|
+
interceptor: "camelCase + .interceptor.ts suffix"
|
|
80
|
+
model: "PascalCase + .model.ts suffix"
|
|
81
|
+
interface: "PascalCase (no I prefix)"
|
|
82
|
+
enum: "PascalCase"
|
|
83
|
+
constant: "SCREAMING_SNAKE_CASE"
|
|
84
|
+
|
|
85
|
+
selectors:
|
|
86
|
+
component: "app-feature-name (kebab-case with app prefix)"
|
|
87
|
+
directive: "appDirectiveName (camelCase with app prefix)"
|
|
88
|
+
|
|
89
|
+
files:
|
|
90
|
+
component: "feature-name.component.ts"
|
|
91
|
+
template: "feature-name.component.html"
|
|
92
|
+
styles: "feature-name.component.scss"
|
|
93
|
+
spec: "feature-name.component.spec.ts"
|
|
94
|
+
service: "feature-name.service.ts"
|
|
95
|
+
module: "feature-name.module.ts (legacy only)"
|
|
96
|
+
|
|
97
|
+
folders:
|
|
98
|
+
feature: "kebab-case"
|
|
99
|
+
shared: "kebab-case"
|
|
100
|
+
|
|
101
|
+
# ----------------------------------------------------------------------------
|
|
102
|
+
# ANGULAR 19+ MODERN PATTERNS
|
|
103
|
+
# ----------------------------------------------------------------------------
|
|
104
|
+
modernPatterns:
|
|
105
|
+
standaloneComponents:
|
|
106
|
+
required: true
|
|
107
|
+
description: "All new components must be standalone"
|
|
108
|
+
example: |
|
|
109
|
+
@Component({
|
|
110
|
+
selector: 'app-user-card',
|
|
111
|
+
standalone: true,
|
|
112
|
+
imports: [RouterLink],
|
|
113
|
+
template: `...`
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
signals:
|
|
117
|
+
required: true
|
|
118
|
+
description: "Use signals for component state (all stable in v20+)"
|
|
119
|
+
patterns:
|
|
120
|
+
- "signal() for writable state"
|
|
121
|
+
- "computed() for derived state"
|
|
122
|
+
- "linkedSignal() for derived state that can be overwritten"
|
|
123
|
+
- "effect() for side effects (sparingly)"
|
|
124
|
+
- "input() for component inputs (signal-based)"
|
|
125
|
+
- "output() for component outputs"
|
|
126
|
+
- "model() for two-way binding"
|
|
127
|
+
- "viewChild() / viewChildren() for queries (signal-based)"
|
|
128
|
+
- "contentChild() / contentChildren() for content queries"
|
|
129
|
+
example: |
|
|
130
|
+
// Component state
|
|
131
|
+
count = signal(0);
|
|
132
|
+
doubleCount = computed(() => this.count() * 2);
|
|
133
|
+
|
|
134
|
+
// Linked signal (can be reset/overwritten)
|
|
135
|
+
selectedId = linkedSignal(() => this.items()[0]?.id);
|
|
136
|
+
|
|
137
|
+
// Signal-based inputs/outputs
|
|
138
|
+
name = input.required<string>();
|
|
139
|
+
clicked = output<void>();
|
|
140
|
+
|
|
141
|
+
// Signal-based queries
|
|
142
|
+
inputRef = viewChild<ElementRef>('inputElement');
|
|
143
|
+
|
|
144
|
+
zoneless:
|
|
145
|
+
recommended: true
|
|
146
|
+
description: "Zoneless change detection (stable in v20.2+)"
|
|
147
|
+
benefits:
|
|
148
|
+
- "60% improvement in startup time"
|
|
149
|
+
- "Better Core Web Vitals"
|
|
150
|
+
- "Smaller bundle size (no zone.js)"
|
|
151
|
+
- "Cleaner stack traces"
|
|
152
|
+
- "No patching for new browser APIs"
|
|
153
|
+
setup: |
|
|
154
|
+
// app.config.ts
|
|
155
|
+
export const appConfig: ApplicationConfig = {
|
|
156
|
+
providers: [
|
|
157
|
+
provideExperimentalZonelessChangeDetection()
|
|
158
|
+
]
|
|
159
|
+
};
|
|
160
|
+
rules:
|
|
161
|
+
- "Remove zone.js from angular.json build and test"
|
|
162
|
+
- "Use signals or call markForCheck() to trigger CD"
|
|
163
|
+
- "Avoid NgZone.onMicrotaskEmpty, onUnstable, onStable"
|
|
164
|
+
- "AsyncPipe works automatically"
|
|
165
|
+
- "OnPush not required but recommended"
|
|
166
|
+
|
|
167
|
+
controlFlow:
|
|
168
|
+
required: true
|
|
169
|
+
description: "Use new built-in control flow"
|
|
170
|
+
patterns:
|
|
171
|
+
- "@if / @else instead of *ngIf"
|
|
172
|
+
- "@for with track instead of *ngFor"
|
|
173
|
+
- "@switch / @case instead of ngSwitch"
|
|
174
|
+
- "@defer for lazy loading"
|
|
175
|
+
example: |
|
|
176
|
+
@if (user()) {
|
|
177
|
+
<app-user-card [user]="user()" />
|
|
178
|
+
} @else {
|
|
179
|
+
<app-loading />
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@for (item of items(); track item.id) {
|
|
183
|
+
<app-item [data]="item" />
|
|
184
|
+
} @empty {
|
|
185
|
+
<p>No items found</p>
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
deferredLoading:
|
|
189
|
+
description: "Use @defer for performance optimization"
|
|
190
|
+
triggers:
|
|
191
|
+
- "@defer (on viewport) - lazy load when visible"
|
|
192
|
+
- "@defer (on interaction) - lazy load on click/focus"
|
|
193
|
+
- "@defer (on idle) - lazy load when browser idle"
|
|
194
|
+
- "@defer (when condition) - lazy load when condition true"
|
|
195
|
+
|
|
196
|
+
inject:
|
|
197
|
+
required: true
|
|
198
|
+
description: "Prefer inject() function over constructor injection"
|
|
199
|
+
example: |
|
|
200
|
+
// Preferred
|
|
201
|
+
private userService = inject(UserService);
|
|
202
|
+
private router = inject(Router);
|
|
203
|
+
|
|
204
|
+
// Avoid (legacy)
|
|
205
|
+
constructor(private userService: UserService) {}
|
|
206
|
+
|
|
207
|
+
# ----------------------------------------------------------------------------
|
|
208
|
+
# STATE MANAGEMENT
|
|
209
|
+
# ----------------------------------------------------------------------------
|
|
210
|
+
stateManagement:
|
|
211
|
+
local:
|
|
212
|
+
tool: "Signals"
|
|
213
|
+
useWhen:
|
|
214
|
+
- "Component-specific state"
|
|
215
|
+
- "Form state"
|
|
216
|
+
- "UI state (modals, toggles)"
|
|
217
|
+
example: |
|
|
218
|
+
isOpen = signal(false);
|
|
219
|
+
toggle() { this.isOpen.update(v => !v); }
|
|
220
|
+
|
|
221
|
+
shared:
|
|
222
|
+
tool: "NgRx SignalStore or Services with Signals"
|
|
223
|
+
useWhen:
|
|
224
|
+
- "State shared across multiple components"
|
|
225
|
+
- "Complex state logic"
|
|
226
|
+
- "State that persists across routes"
|
|
227
|
+
patterns:
|
|
228
|
+
- "SignalStore for feature state"
|
|
229
|
+
- "Service + signals for simple shared state"
|
|
230
|
+
- "Avoid BehaviorSubject for new code"
|
|
231
|
+
|
|
232
|
+
server:
|
|
233
|
+
tool: "TanStack Query (Angular Query) or HttpClient + Signals"
|
|
234
|
+
useWhen:
|
|
235
|
+
- "API data fetching"
|
|
236
|
+
- "Caching server state"
|
|
237
|
+
- "Optimistic updates"
|
|
238
|
+
|
|
239
|
+
rules:
|
|
240
|
+
- "Signals over RxJS for synchronous state"
|
|
241
|
+
- "RxJS for async streams (HTTP, WebSocket, events)"
|
|
242
|
+
- "toSignal() to convert Observable to Signal"
|
|
243
|
+
- "toObservable() when you need RxJS operators"
|
|
244
|
+
|
|
245
|
+
# ----------------------------------------------------------------------------
|
|
246
|
+
# RXJS GUIDELINES
|
|
247
|
+
# ----------------------------------------------------------------------------
|
|
248
|
+
rxjs:
|
|
249
|
+
usage: "Async operations only"
|
|
250
|
+
patterns:
|
|
251
|
+
- "Use async pipe in templates (auto-unsubscribe)"
|
|
252
|
+
- "Convert to signals with toSignal() when possible"
|
|
253
|
+
- "takeUntilDestroyed() for manual subscriptions"
|
|
254
|
+
- "Avoid nested subscribes"
|
|
255
|
+
|
|
256
|
+
operators:
|
|
257
|
+
preferred:
|
|
258
|
+
- "switchMap for cancellable requests"
|
|
259
|
+
- "concatMap for sequential operations"
|
|
260
|
+
- "mergeMap for parallel operations"
|
|
261
|
+
- "exhaustMap for ignoring while processing"
|
|
262
|
+
- "debounceTime for user input"
|
|
263
|
+
- "distinctUntilChanged to prevent duplicates"
|
|
264
|
+
- "catchError for error handling"
|
|
265
|
+
avoid:
|
|
266
|
+
- "subscribe() in components when async pipe works"
|
|
267
|
+
- "Nested subscribes (use higher-order operators)"
|
|
268
|
+
- "Manual unsubscribe (use takeUntilDestroyed)"
|
|
269
|
+
|
|
270
|
+
# ----------------------------------------------------------------------------
|
|
271
|
+
# TYPESCRIPT
|
|
272
|
+
# ----------------------------------------------------------------------------
|
|
273
|
+
technologies:
|
|
274
|
+
- name: typescript
|
|
275
|
+
version: "5.x"
|
|
276
|
+
specificRules:
|
|
277
|
+
strict: true
|
|
278
|
+
noImplicitAny: true
|
|
279
|
+
strictNullChecks: true
|
|
280
|
+
noUnusedLocals: true
|
|
281
|
+
noUnusedParameters: true
|
|
282
|
+
exactOptionalPropertyTypes: true
|
|
283
|
+
|
|
284
|
+
- name: angular
|
|
285
|
+
version: "19+"
|
|
286
|
+
specificRules:
|
|
287
|
+
standalone: true
|
|
288
|
+
signals: true
|
|
289
|
+
newControlFlow: true
|
|
290
|
+
onPushDefault: true
|
|
291
|
+
zoneless: "recommended (stable in v20.2+)"
|
|
292
|
+
signalForms: "experimental (v21+)"
|
|
293
|
+
|
|
294
|
+
- name: linting
|
|
295
|
+
tools:
|
|
296
|
+
- "ESLint"
|
|
297
|
+
- "@angular-eslint"
|
|
298
|
+
- "Prettier"
|
|
299
|
+
|
|
300
|
+
# ----------------------------------------------------------------------------
|
|
301
|
+
# TESTING
|
|
302
|
+
# ----------------------------------------------------------------------------
|
|
303
|
+
testing:
|
|
304
|
+
framework: "Jest or Vitest"
|
|
305
|
+
assertionLibrary: "Jest/Vitest matchers"
|
|
306
|
+
componentTesting: "Angular Testing Library"
|
|
307
|
+
|
|
308
|
+
types:
|
|
309
|
+
unit:
|
|
310
|
+
suffix: ".spec.ts"
|
|
311
|
+
location: "co-located with source"
|
|
312
|
+
coverage: 70
|
|
313
|
+
|
|
314
|
+
component:
|
|
315
|
+
suffix: ".spec.ts"
|
|
316
|
+
location: "co-located"
|
|
317
|
+
tool: "Angular Testing Library"
|
|
318
|
+
patterns:
|
|
319
|
+
- "Test user interactions, not implementation"
|
|
320
|
+
- "Use screen queries (getByRole, getByText)"
|
|
321
|
+
- "Avoid testing internal component state"
|
|
322
|
+
|
|
323
|
+
integration:
|
|
324
|
+
suffix: ".integration.spec.ts"
|
|
325
|
+
location: "tests/integration"
|
|
326
|
+
|
|
327
|
+
e2e:
|
|
328
|
+
tool: "Playwright or Cypress"
|
|
329
|
+
location: "e2e/"
|
|
330
|
+
|
|
331
|
+
patterns:
|
|
332
|
+
arrange_act_assert: true
|
|
333
|
+
behavior_testing: true
|
|
334
|
+
mock_services: true
|
|
335
|
+
testing_library_queries: true
|
|
336
|
+
|
|
337
|
+
rules:
|
|
338
|
+
- "Prefer Angular Testing Library over TestBed directly"
|
|
339
|
+
- "Test user-visible behavior"
|
|
340
|
+
- "Avoid implementation details (internal state, private methods)"
|
|
341
|
+
- "Use jest-preset-angular or analog for Vitest"
|
|
342
|
+
|
|
343
|
+
# ----------------------------------------------------------------------------
|
|
344
|
+
# STYLING
|
|
345
|
+
# ----------------------------------------------------------------------------
|
|
346
|
+
styling:
|
|
347
|
+
approach: "Component-scoped SCSS or Tailwind CSS"
|
|
348
|
+
tools:
|
|
349
|
+
- "SCSS (default)"
|
|
350
|
+
- "Tailwind CSS"
|
|
351
|
+
- "CSS Variables for theming"
|
|
352
|
+
|
|
353
|
+
rules:
|
|
354
|
+
- "Use ViewEncapsulation.Emulated (default)"
|
|
355
|
+
- "Avoid ViewEncapsulation.None"
|
|
356
|
+
- ":host for component root styles"
|
|
357
|
+
- "CSS custom properties for theming"
|
|
358
|
+
- "Mobile-first responsive design"
|
|
359
|
+
- "BEM naming for complex components (optional with Tailwind)"
|
|
360
|
+
|
|
361
|
+
# ----------------------------------------------------------------------------
|
|
362
|
+
# PERFORMANCE
|
|
363
|
+
# ----------------------------------------------------------------------------
|
|
364
|
+
performance:
|
|
365
|
+
changeDetection:
|
|
366
|
+
- "OnPush by default for all components"
|
|
367
|
+
- "Use signals to minimize change detection"
|
|
368
|
+
- "Avoid function calls in templates"
|
|
369
|
+
- "Use pure pipes for transformations"
|
|
370
|
+
|
|
371
|
+
lazyLoading:
|
|
372
|
+
- "Lazy load routes with loadComponent/loadChildren"
|
|
373
|
+
- "Use @defer for below-fold content"
|
|
374
|
+
- "Preload strategies for critical routes"
|
|
375
|
+
|
|
376
|
+
bundling:
|
|
377
|
+
- "Use esbuild (default in Angular 17+)"
|
|
378
|
+
- "Analyze bundle with source-map-explorer"
|
|
379
|
+
- "Tree-shake unused code"
|
|
380
|
+
|
|
381
|
+
rules:
|
|
382
|
+
- "trackBy function for @for loops (now 'track' expression)"
|
|
383
|
+
- "Lazy load images with loading='lazy'"
|
|
384
|
+
- "Use OnPush + signals for optimal performance"
|
|
385
|
+
- "Avoid unnecessary template re-evaluations"
|
|
386
|
+
|
|
387
|
+
# ----------------------------------------------------------------------------
|
|
388
|
+
# ROUTING
|
|
389
|
+
# ----------------------------------------------------------------------------
|
|
390
|
+
routing:
|
|
391
|
+
patterns:
|
|
392
|
+
- "Functional guards (canActivate, canMatch)"
|
|
393
|
+
- "Functional resolvers"
|
|
394
|
+
- "Lazy loading with loadComponent"
|
|
395
|
+
- "Route-level providers"
|
|
396
|
+
|
|
397
|
+
example: |
|
|
398
|
+
export const routes: Routes = [
|
|
399
|
+
{
|
|
400
|
+
path: 'users',
|
|
401
|
+
loadComponent: () => import('./users/users.component'),
|
|
402
|
+
canActivate: [authGuard],
|
|
403
|
+
providers: [UserService]
|
|
404
|
+
}
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
# ----------------------------------------------------------------------------
|
|
408
|
+
# HTTP & API
|
|
409
|
+
# ----------------------------------------------------------------------------
|
|
410
|
+
api:
|
|
411
|
+
client: "HttpClient with interceptors"
|
|
412
|
+
patterns:
|
|
413
|
+
- "Centralize API configuration"
|
|
414
|
+
- "Use functional interceptors"
|
|
415
|
+
- "Type all responses"
|
|
416
|
+
- "Handle errors globally"
|
|
417
|
+
|
|
418
|
+
interceptors:
|
|
419
|
+
- "Auth token injection"
|
|
420
|
+
- "Error handling"
|
|
421
|
+
- "Loading state"
|
|
422
|
+
- "Retry logic"
|
|
423
|
+
|
|
424
|
+
example: |
|
|
425
|
+
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
426
|
+
const token = inject(AuthService).token();
|
|
427
|
+
if (token) {
|
|
428
|
+
req = req.clone({
|
|
429
|
+
setHeaders: { Authorization: `Bearer ${token}` }
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return next(req);
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
# ----------------------------------------------------------------------------
|
|
436
|
+
# FORMS
|
|
437
|
+
# ----------------------------------------------------------------------------
|
|
438
|
+
forms:
|
|
439
|
+
preferred: "Reactive Forms"
|
|
440
|
+
patterns:
|
|
441
|
+
- "FormBuilder for complex forms"
|
|
442
|
+
- "Typed forms (FormGroup<T>)"
|
|
443
|
+
- "Custom validators as functions"
|
|
444
|
+
- "Async validators for server validation"
|
|
445
|
+
|
|
446
|
+
rules:
|
|
447
|
+
- "Prefer Reactive Forms over Template-driven"
|
|
448
|
+
- "Use FormControl signals (.value signal in Angular 18+)"
|
|
449
|
+
- "Centralize validation messages"
|
|
450
|
+
- "Handle form state (pristine, touched, valid)"
|
|
451
|
+
|
|
452
|
+
# ----------------------------------------------------------------------------
|
|
453
|
+
# PROJECT STRUCTURE
|
|
454
|
+
# ----------------------------------------------------------------------------
|
|
455
|
+
# Recommended folder structure:
|
|
456
|
+
#
|
|
457
|
+
# src/app/
|
|
458
|
+
# ├── core/ # Singleton services, guards, interceptors
|
|
459
|
+
# │ ├── services/
|
|
460
|
+
# │ │ ├── auth.service.ts
|
|
461
|
+
# │ │ └── api.service.ts
|
|
462
|
+
# │ ├── guards/
|
|
463
|
+
# │ │ └── auth.guard.ts
|
|
464
|
+
# │ ├── interceptors/
|
|
465
|
+
# │ │ └── auth.interceptor.ts
|
|
466
|
+
# │ └── models/
|
|
467
|
+
# │ └── user.model.ts
|
|
468
|
+
# │
|
|
469
|
+
# ├── shared/ # Reusable standalone components
|
|
470
|
+
# │ ├── components/
|
|
471
|
+
# │ │ ├── button/
|
|
472
|
+
# │ │ │ ├── button.component.ts
|
|
473
|
+
# │ │ │ └── button.component.spec.ts
|
|
474
|
+
# │ │ └── modal/
|
|
475
|
+
# │ ├── directives/
|
|
476
|
+
# │ ├── pipes/
|
|
477
|
+
# │ └── utils/
|
|
478
|
+
# │
|
|
479
|
+
# ├── features/ # Feature modules (standalone)
|
|
480
|
+
# │ ├── auth/
|
|
481
|
+
# │ │ ├── login/
|
|
482
|
+
# │ │ │ ├── login.component.ts
|
|
483
|
+
# │ │ │ └── login.component.spec.ts
|
|
484
|
+
# │ │ ├── register/
|
|
485
|
+
# │ │ └── auth.routes.ts
|
|
486
|
+
# │ ├── dashboard/
|
|
487
|
+
# │ └── settings/
|
|
488
|
+
# │
|
|
489
|
+
# ├── app.component.ts # Root standalone component
|
|
490
|
+
# ├── app.config.ts # Application configuration
|
|
491
|
+
# └── app.routes.ts # Root routes
|
|
492
|
+
#
|
|
493
|
+
# e2e/ # End-to-end tests
|
|
494
|
+
# └── specs/
|