@tungvivas/angular-vibe-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/bin/cli.js +245 -0
- package/commands/dev-cycle.md +139 -0
- package/commands/init.md +103 -0
- package/commands/new-feature.md +69 -0
- package/commands/review-pr.md +63 -0
- package/commands/start.md +21 -0
- package/commands/update-status.md +19 -0
- package/commands/write-context.md +71 -0
- package/commands/write-tests.md +110 -0
- package/package.json +40 -0
- package/practices/v12-13.md +193 -0
- package/practices/v14-15.md +133 -0
- package/practices/v16.md +127 -0
- package/practices/v17.md +166 -0
- package/practices/v18-19.md +121 -0
- package/practices/v20plus.md +210 -0
- package/templates/CLAUDE.md +55 -0
- package/templates/docs/API_CONTRACT.md +46 -0
- package/templates/docs/ARCHITECTURE.md +27 -0
- package/templates/docs/DESIGN_SYSTEM.md +27 -0
- package/templates/docs/PROJECT-STATUS.md +19 -0
- package/templates/docs/decisions/001-state-management.md +18 -0
- package/templates/docs/decisions/002-auth-token-storage.md +19 -0
- package/templates/rules/project-rules.md +72 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Create a new feature module. Follow these steps in exact order.
|
|
2
|
+
|
|
3
|
+
Angular idioms depend on the project version — read `.claude/angular-practices/`
|
|
4
|
+
and follow it. **Precedence (see `.claude/rules/project-rules.md` → Precedence):** a valid
|
|
5
|
+
project convention wins over the BP profile; the profile applies only where the project
|
|
6
|
+
has no convention or is below standard (new code only); legacy modules are never refactored.
|
|
7
|
+
|
|
8
|
+
## Step 1: Understand Context
|
|
9
|
+
- Read `CLAUDE.md` → links to docs below
|
|
10
|
+
- Read `.claude/rules/project-rules.md` → naming, structure, Coexistence rules, **Reference Examples**
|
|
11
|
+
- Read `docs/ARCHITECTURE.md` → where this feature fits
|
|
12
|
+
- Read `docs/API_CONTRACT.md` → which endpoints this feature calls
|
|
13
|
+
- Read `docs/DESIGN_SYSTEM.md` → shared components to use
|
|
14
|
+
- **Open the Reference Example files** listed in `project-rules.md` (best existing service / page / component / spec) and mirror their style — this is the strongest signal for matching the project's real conventions
|
|
15
|
+
- Ask me if anything is unclear before writing code
|
|
16
|
+
|
|
17
|
+
## Step 2: Create Feature Structure
|
|
18
|
+
|
|
19
|
+
> **Use the project's actual layout and folder names from `project-rules.md`.**
|
|
20
|
+
> The tree below is only the DEFAULT shape — if the project calls things differently
|
|
21
|
+
> (e.g. `containers/` instead of `pages/`, `ui/` or `common/` instead of `shared/`,
|
|
22
|
+
> an Nx `libs/` layout, or NgModule-based folders), follow the project, not this tree.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
{features-dir}/{feature-name}/ # e.g. features/, modules/, or an Nx lib
|
|
26
|
+
├── {feature}.routes.ts
|
|
27
|
+
├── models/
|
|
28
|
+
│ └── {feature}.model.ts # Interfaces mirroring the backend response shape
|
|
29
|
+
├── services/
|
|
30
|
+
│ └── {feature}.service.ts # All data-access calls
|
|
31
|
+
├── pages/ # Smart (container) components — project may call these containers/
|
|
32
|
+
│ ├── {feature}-list/
|
|
33
|
+
│ │ ├── {feature}-list.component.ts
|
|
34
|
+
│ │ ├── {feature}-list.component.html
|
|
35
|
+
│ │ └── {feature}-list.component.scss
|
|
36
|
+
│ └── {feature}-detail/
|
|
37
|
+
└── components/ # Dumb (presentational) components
|
|
38
|
+
└── {feature}-form/
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Step 3: Implement in This Order
|
|
42
|
+
1. **Model** — interfaces for entity, request/response, mirroring the backend's actual DTOs / response shape (per `project-rules.md` — envelope, raw DTO, or GraphQL type)
|
|
43
|
+
2. **Service** — data-access methods returning `Observable<T>` typed with the project's response shape (HttpClient, or the project's client if it uses Apollo/another)
|
|
44
|
+
3. **Routes** — lazy-loaded, guard-protected where needed
|
|
45
|
+
4. **Page components (smart)** — data, loading/error/empty states
|
|
46
|
+
5. **Presentational components (dumb)** — inputs/outputs only, no data access
|
|
47
|
+
|
|
48
|
+
## Step 4: Write Tests (see /write-tests)
|
|
49
|
+
- Service: mock HttpClient, test success + error + empty per method
|
|
50
|
+
- Component: mock service, test render + interaction + loading + error states
|
|
51
|
+
|
|
52
|
+
## Step 5: Rules (from .claude/rules/project-rules.md + .claude/angular-practices/)
|
|
53
|
+
- Data access ONLY in services — never in components
|
|
54
|
+
- `ChangeDetectionStrategy.OnPush` on every component
|
|
55
|
+
- Tear down subscriptions per profile: `takeUntilDestroyed` (v16+) or `takeUntil(destroy$)` + `ngOnDestroy` (v12–15)
|
|
56
|
+
- No `any` — interfaces/types for every model and response
|
|
57
|
+
- Use the project's forms approach from `project-rules.md` (Reactive Forms by default; Formly / template-driven if that's what the project uses)
|
|
58
|
+
- No hardcoded backend URL — use `environment`
|
|
59
|
+
|
|
60
|
+
## Step 6: Update Documentation
|
|
61
|
+
- `docs/API_CONTRACT.md` if you used/discovered endpoints
|
|
62
|
+
- Create `CONTEXT.md` inside the feature folder (use /write-context)
|
|
63
|
+
- `docs/PROJECT-STATUS.md` (use /update-status)
|
|
64
|
+
|
|
65
|
+
## Step 7: Verify
|
|
66
|
+
- Build passes: `ng build` / `npx tsc --noEmit`
|
|
67
|
+
- All tests pass: `ng test` / `jest`
|
|
68
|
+
- Lint passes: `ng lint`
|
|
69
|
+
- Review with /review-pr before committing
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Review the code changes in this PR. Check every item — flag violations clearly.
|
|
2
|
+
|
|
3
|
+
Read `.claude/angular-practices/` for version-correct idioms, and
|
|
4
|
+
`.claude/rules/project-rules.md` for project-specific rules.
|
|
5
|
+
|
|
6
|
+
**Precedence (see `project-rules.md` → Precedence):** a valid project convention wins over
|
|
7
|
+
the BP profile; flag a BP deviation as a blocker only when it's an objective defect in NEW
|
|
8
|
+
code. Do NOT flag legacy modules listed in the Coexistence Strategy — they are kept as-is.
|
|
9
|
+
Do NOT flag a valid project choice (naming, structure, state lib, response shape) just
|
|
10
|
+
because it differs from the profile.
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
- [ ] Smart / dumb separation: pages own data; presentational components only render + emit
|
|
14
|
+
- [ ] HTTP calls ONLY in services — never in components
|
|
15
|
+
- [ ] No business logic in templates
|
|
16
|
+
- [ ] Feature routes are lazy-loaded
|
|
17
|
+
|
|
18
|
+
## Type Safety
|
|
19
|
+
- [ ] No `any` — interfaces/types for every model and API response
|
|
20
|
+
- [ ] Responses typed with the project's response shape recorded in `project-rules.md` (e.g. an `ApiResponse<T>` envelope, a raw DTO, or a GraphQL type — whatever the backend actually returns)
|
|
21
|
+
- [ ] No `@ts-ignore` / `@ts-expect-error`
|
|
22
|
+
|
|
23
|
+
## Dependency Injection (follow `.claude/angular-practices/`)
|
|
24
|
+
- [ ] DI matches the profile: `inject()` (v16+) or constructor injection (v12–15) — consistent across the project
|
|
25
|
+
- [ ] Services `@Injectable({ providedIn: 'root' })`
|
|
26
|
+
|
|
27
|
+
## Reactive / RxJS / Signals (follow `.claude/angular-practices/`)
|
|
28
|
+
- [ ] Subscriptions torn down: `takeUntilDestroyed` (v16+) or `takeUntil(destroy$)` + `ngOnDestroy` (v12–15)
|
|
29
|
+
- [ ] `async` pipe preferred over manual subscribe
|
|
30
|
+
- [ ] No `toPromise()` — use `firstValueFrom()` if needed
|
|
31
|
+
- [ ] Services return `Observable<T>` — do NOT subscribe internally
|
|
32
|
+
- [ ] Signals used per profile: stable for state (v16+); derived state via `computed()`. `resource`/`httpResource` are experimental (v19+) — only if the team opted in
|
|
33
|
+
|
|
34
|
+
## Error Handling
|
|
35
|
+
- [ ] Loading / error / empty states handled in list/detail pages
|
|
36
|
+
- [ ] HTTP errors surfaced to user (not swallowed)
|
|
37
|
+
- [ ] Auth errors (e.g. 401) handled centrally by an interceptor — not per-feature
|
|
38
|
+
|
|
39
|
+
## Security
|
|
40
|
+
- [ ] Auth handled per the project's model in `project-rules.md` (token in memory / httpOnly cookie / session / OAuth) — and consistent with it. If tokens are used, they are NOT in localStorage
|
|
41
|
+
- [ ] No hardcoded backend URL — uses `environment`
|
|
42
|
+
- [ ] Routes needing auth are guarded
|
|
43
|
+
- [ ] No unsanitized HTML (`innerHTML` / `bypassSecurityTrust*`) without a clear, reviewed reason
|
|
44
|
+
|
|
45
|
+
## Performance
|
|
46
|
+
- [ ] `ChangeDetectionStrategy.OnPush` on every component (or zoneless on v20+ if enabled)
|
|
47
|
+
- [ ] List tracking present: `@for` with `track` (v17+) or `*ngFor` with `trackBy` (v12–16)
|
|
48
|
+
|
|
49
|
+
## Code Quality
|
|
50
|
+
- [ ] Component file < ~300 lines; method < ~50 lines
|
|
51
|
+
- [ ] No dead code, no `console.log`
|
|
52
|
+
- [ ] Naming follows `.claude/rules/project-rules.md`
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
- [ ] New feature: `CONTEXT.md` created inside feature folder
|
|
56
|
+
- [ ] Logic changed: `CONTEXT.md` Refactor Log updated (append only)
|
|
57
|
+
- [ ] Endpoint changed: `docs/API_CONTRACT.md` updated
|
|
58
|
+
- [ ] `docs/PROJECT-STATUS.md` updated
|
|
59
|
+
|
|
60
|
+
## Summary
|
|
61
|
+
1. **Blockers** 🔴 — must fix before merge
|
|
62
|
+
2. **Suggestions** 🟡 — improve but not blocking
|
|
63
|
+
3. **Good parts** 🟢 — what was done well
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Start of a new coding session. Read context before doing anything.
|
|
2
|
+
|
|
3
|
+
## Read These (always)
|
|
4
|
+
- Read `CLAUDE.md` → project overview and links to docs
|
|
5
|
+
- Read `docs/PROJECT-STATUS.md` → current progress, warnings, next tasks
|
|
6
|
+
|
|
7
|
+
## Read These (only when needed)
|
|
8
|
+
- `.claude/rules/project-rules.md` → ONLY if this is your first session OR you're about to write code
|
|
9
|
+
- `docs/ARCHITECTURE.md` → ONLY if task involves cross-module work or a new feature
|
|
10
|
+
- `docs/API_CONTRACT.md` → ONLY if task involves calling backend endpoints
|
|
11
|
+
- `docs/DESIGN_SYSTEM.md` → ONLY if task involves UI/components
|
|
12
|
+
- A feature's `CONTEXT.md` → ONLY if you're modifying that feature
|
|
13
|
+
|
|
14
|
+
## After Reading, Tell Me
|
|
15
|
+
1. What was last completed
|
|
16
|
+
2. What is in progress (if any)
|
|
17
|
+
3. Any active warnings
|
|
18
|
+
4. Suggested next task
|
|
19
|
+
|
|
20
|
+
## Then Wait
|
|
21
|
+
Do NOT start coding. Wait for me to confirm or change the task.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Update `docs/PROJECT-STATUS.md` based on what we did this session.
|
|
2
|
+
|
|
3
|
+
Rules:
|
|
4
|
+
1. Read the current `docs/PROJECT-STATUS.md` first
|
|
5
|
+
2. Move completed items from "In Progress" → "Completed" (with date)
|
|
6
|
+
3. Add any new work done this session to "Completed"
|
|
7
|
+
4. Update "In Progress" with anything still unfinished (note which component/file)
|
|
8
|
+
5. Add discovered issues to "Deferred Issues" with priority [P1/P2/P3]
|
|
9
|
+
6. Add gotchas to "Warnings"
|
|
10
|
+
7. Update "Next Tasks" — reorder by current priority
|
|
11
|
+
8. Update the header: date, session number (increment by 1)
|
|
12
|
+
9. Keep the file under 80 lines — archive old "Completed" items if too long
|
|
13
|
+
|
|
14
|
+
After updating, commit code and docs together:
|
|
15
|
+
```
|
|
16
|
+
git add CLAUDE.md docs/ .claude/rules/ src/
|
|
17
|
+
git commit -m "feat: {what was built} + tests + docs"
|
|
18
|
+
```
|
|
19
|
+
Docs and code in the same commit — docs that lag behind code go stale.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Write a `CONTEXT.md` file for the current feature module.
|
|
2
|
+
|
|
3
|
+
This file is a **snapshot** — it captures WHY this module was built this way,
|
|
4
|
+
at the time it was built. It helps future developers (and AI) understand decisions
|
|
5
|
+
without guessing from code.
|
|
6
|
+
|
|
7
|
+
## Where to Create
|
|
8
|
+
Place the file inside the feature folder, using the project's actual layout from
|
|
9
|
+
`project-rules.md` (the `features/` path below is only the default example):
|
|
10
|
+
```
|
|
11
|
+
{features-dir}/{feature-name}/CONTEXT.md # e.g. features/, modules/, or an Nx lib
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Template — Fill Every Section
|
|
15
|
+
|
|
16
|
+
```markdown
|
|
17
|
+
# {Feature Name} — Implementation Context
|
|
18
|
+
> Written: {YYYY-MM-DD} | Author: {who built it}
|
|
19
|
+
|
|
20
|
+
## Business Context
|
|
21
|
+
Why does this module exist? What user/business problem does it solve?
|
|
22
|
+
(2-3 sentences max)
|
|
23
|
+
|
|
24
|
+
## Components
|
|
25
|
+
- `{Feature}ListPage` — smart component: fetches data, pagination, handles loading/error/empty
|
|
26
|
+
- `{Feature}DetailPage` — smart component: create/edit form (per the project's forms approach), submit
|
|
27
|
+
- `{Feature}FormComponent` — dumb component: receives form/inputs, emits events
|
|
28
|
+
- `{Feature}CardComponent` — dumb component: renders one item
|
|
29
|
+
|
|
30
|
+
## Service
|
|
31
|
+
`{Feature}Service` — all HTTP calls to `{base endpoint}`
|
|
32
|
+
|
|
33
|
+
## State Management
|
|
34
|
+
Which approach and WHY (Signals / NgRx / BehaviorSubject / plain service).
|
|
35
|
+
Note what is local component state vs shared state.
|
|
36
|
+
|
|
37
|
+
## API Endpoints Used
|
|
38
|
+
- GET {path}?page=&size= → paginated list
|
|
39
|
+
- GET {path}/:id → single
|
|
40
|
+
- POST {path} → create
|
|
41
|
+
- PUT {path}/:id → update
|
|
42
|
+
- DELETE {path}/:id → delete
|
|
43
|
+
|
|
44
|
+
## Technical Decisions
|
|
45
|
+
- **{Decision}**: {reason}
|
|
46
|
+
- **{Decision}**: {reason}
|
|
47
|
+
|
|
48
|
+
## Considered and Rejected
|
|
49
|
+
- **{Option}**: rejected because {reason}
|
|
50
|
+
|
|
51
|
+
## Dependencies
|
|
52
|
+
- Depends on: {other modules/services this uses}
|
|
53
|
+
- Depended by: {modules that use this}
|
|
54
|
+
|
|
55
|
+
## Known Limitations
|
|
56
|
+
- ⚠️ {limitation — explain why it's acceptable for now}
|
|
57
|
+
|
|
58
|
+
## Refactor Log
|
|
59
|
+
(Add entries here when significant changes are made. Do NOT edit sections above.)
|
|
60
|
+
|
|
61
|
+
### {YYYY-MM-DD} | {author}
|
|
62
|
+
- {what changed and why}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
1. Write in clear language
|
|
67
|
+
2. Be specific — "chose Signals because state is local to this page", not just "chose Signals"
|
|
68
|
+
3. Keep each section concise (2-5 bullet points max)
|
|
69
|
+
4. "Known Limitations" is critical — prevents future devs from "fixing" intentional trade-offs
|
|
70
|
+
5. NEVER edit old content — only ADD new entries in the Refactor Log
|
|
71
|
+
6. Write it NOW while context is fresh — tomorrow you'll forget half the reasons
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Write tests for the specified feature module. Read the source code first, then generate tests.
|
|
2
|
+
|
|
3
|
+
Use the test runner the project already uses (Jest or Karma/Jasmine — check
|
|
4
|
+
`package.json` / `angular.json`). Read `.claude/angular-practices/` for the
|
|
5
|
+
version's testing idioms.
|
|
6
|
+
|
|
7
|
+
## Step 1: Read Before Writing
|
|
8
|
+
- Read the feature's source: model, service, page components, presentational components
|
|
9
|
+
- Read the module's `CONTEXT.md` (if it exists) to understand trade-offs and edge cases
|
|
10
|
+
- Read `docs/API_CONTRACT.md` for expected request/response shapes
|
|
11
|
+
- Identify all code paths: happy path, error cases, empty states, validation
|
|
12
|
+
|
|
13
|
+
## Step 2: Unit Tests — `{feature}.service.spec.ts`
|
|
14
|
+
|
|
15
|
+
Use `HttpClientTestingModule` + `HttpTestingController` (works on every version, v12+).
|
|
16
|
+
On v15+ you may instead use the functional `provideHttpClient()` + `provideHttpClientTesting()`.
|
|
17
|
+
Follow whichever style the project already uses (see `.claude/angular-practices/`).
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
describe('{Feature}Service', () => {
|
|
21
|
+
let service: {Feature}Service;
|
|
22
|
+
let httpMock: HttpTestingController;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
TestBed.configureTestingModule({
|
|
26
|
+
imports: [HttpClientTestingModule],
|
|
27
|
+
providers: [{Feature}Service],
|
|
28
|
+
});
|
|
29
|
+
service = TestBed.inject({Feature}Service);
|
|
30
|
+
httpMock = TestBed.inject(HttpTestingController);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => httpMock.verify());
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Required Coverage Per Method
|
|
38
|
+
**getAll:**
|
|
39
|
+
- ✅ Success — returns list; assert request URL + method `GET`
|
|
40
|
+
- ✅ Empty — returns empty array, not null
|
|
41
|
+
- ✅ Server error — propagates error (500)
|
|
42
|
+
|
|
43
|
+
**getById:**
|
|
44
|
+
- ✅ Found — returns correct response; assert URL contains the id
|
|
45
|
+
- ✅ Not found — 404 error propagated
|
|
46
|
+
|
|
47
|
+
**create:**
|
|
48
|
+
- ✅ Success — assert `POST` + request body matches payload
|
|
49
|
+
- ✅ Validation error — 400
|
|
50
|
+
- ✅ Conflict — 409
|
|
51
|
+
|
|
52
|
+
**update:**
|
|
53
|
+
- ✅ Success — assert `PUT`/`PATCH` + body
|
|
54
|
+
- ✅ Not found — 404
|
|
55
|
+
|
|
56
|
+
**delete:**
|
|
57
|
+
- ✅ Success — assert `DELETE` + URL
|
|
58
|
+
- ✅ Not found — 404
|
|
59
|
+
|
|
60
|
+
### Unit Test Rules
|
|
61
|
+
- Use `httpMock.expectOne(url)` to assert and flush each request
|
|
62
|
+
- Test ONE behavior per test
|
|
63
|
+
- Naming: `methodName_scenario_expectedResult`
|
|
64
|
+
- Describe behavior in the test title, not the implementation
|
|
65
|
+
- `httpMock.verify()` in `afterEach` — no unexpected requests
|
|
66
|
+
- Never hit a real backend
|
|
67
|
+
|
|
68
|
+
## Step 3: Component Tests — `{feature}-list.component.spec.ts`
|
|
69
|
+
|
|
70
|
+
Mock the service with `jasmine.createSpyObj` (Karma) or `jest.fn()` (Jest).
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
describe('{Feature}ListComponent', () => {
|
|
74
|
+
let fixture: ComponentFixture<{Feature}ListComponent>;
|
|
75
|
+
let serviceSpy: jasmine.SpyObj<{Feature}Service>;
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
serviceSpy = jasmine.createSpyObj('{Feature}Service', ['getAll', 'delete']);
|
|
79
|
+
TestBed.configureTestingModule({
|
|
80
|
+
imports: [{Feature}ListComponent],
|
|
81
|
+
providers: [{ provide: {Feature}Service, useValue: serviceSpy }],
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Required Coverage
|
|
88
|
+
- ✅ Render — list shows correct data after load
|
|
89
|
+
- ✅ Loading — spinner visible while the request is pending
|
|
90
|
+
- ✅ Error — error message visible when the service errors
|
|
91
|
+
- ✅ Empty — empty-state message when no data
|
|
92
|
+
- ✅ Interaction — clicking delete calls `service.delete` with the right id
|
|
93
|
+
- ✅ Signals (v16+) — assert signal values update as expected
|
|
94
|
+
|
|
95
|
+
### Component Test Rules
|
|
96
|
+
- Mock the service — never the real HttpClient
|
|
97
|
+
- Drive change detection with `fixture.detectChanges()`
|
|
98
|
+
- Query the DOM via `fixture.nativeElement` / `DebugElement`
|
|
99
|
+
- Each test is independent — no shared mutable state
|
|
100
|
+
|
|
101
|
+
## Step 4: E2E (only for critical flows)
|
|
102
|
+
For login and the main CRUD flow, add a Playwright spec under the project's e2e folder.
|
|
103
|
+
Skip E2E for trivial pages — manual testing covers those.
|
|
104
|
+
|
|
105
|
+
## Step 5: Verify
|
|
106
|
+
- All tests pass: `ng test` / `jest`
|
|
107
|
+
- No test depends on execution order
|
|
108
|
+
- No unit test hits a real backend
|
|
109
|
+
- Test names clearly describe WHAT is tested and the EXPECTED result
|
|
110
|
+
- Coverage: every public service method has at least happy path + error case
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tungvivas/angular-vibe-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bootstrap a Vibe Coding workflow (CLAUDE.md, project docs, slash-commands) into any Angular project. Detects your Angular version and installs version-matched best-practice rules.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"angular",
|
|
7
|
+
"claude",
|
|
8
|
+
"claude-code",
|
|
9
|
+
"ai",
|
|
10
|
+
"scaffold",
|
|
11
|
+
"vibe-coding",
|
|
12
|
+
"slash-commands"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"angular-vibe-kit": "bin/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin/",
|
|
21
|
+
"commands/",
|
|
22
|
+
"practices/",
|
|
23
|
+
"templates/",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=16"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "node bin/cli.js --help",
|
|
32
|
+
"prepublishOnly": "node -c bin/cli.js"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/vuanhtung10/angular-vibe-kit.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/vuanhtung10/angular-vibe-kit#readme",
|
|
39
|
+
"bugs": "https://github.com/vuanhtung10/angular-vibe-kit/issues"
|
|
40
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Angular Best Practices — v12 / v13 (NgModule + NgRx Era)
|
|
2
|
+
|
|
3
|
+
> Framework-level patterns only. This file describes HOW to write idiomatic
|
|
4
|
+
> Angular 12/13 — syntax, DI, components, RxJS. It does NOT dictate folder
|
|
5
|
+
> structure, state-management choice, or naming — those are project decisions
|
|
6
|
+
> recorded in `.claude/rules/project-rules.md` (inferred from the codebase).
|
|
7
|
+
>
|
|
8
|
+
> When the project already follows a correct pattern, follow the project.
|
|
9
|
+
> When it is below standard, apply this standard to NEW code only.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Module System
|
|
14
|
+
- **NgModule is the only option.** Standalone components do NOT exist in v12/13.
|
|
15
|
+
- Every feature has a `*.module.ts` + `*-routing.module.ts`.
|
|
16
|
+
- Feature modules are lazy-loaded via `loadChildren`.
|
|
17
|
+
- `CoreModule` (imported once in AppModule) owns singleton services, interceptors, guards.
|
|
18
|
+
- `SharedModule` owns reusable components, pipes, directives — exported for feature modules.
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
@NgModule({
|
|
22
|
+
declarations: [UserListComponent, UserCardComponent],
|
|
23
|
+
imports: [CommonModule, UserRoutingModule, ReactiveFormsModule, SharedModule],
|
|
24
|
+
providers: [],
|
|
25
|
+
})
|
|
26
|
+
export class UserModule {}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Component Pattern
|
|
30
|
+
- `changeDetection: ChangeDetectionStrategy.OnPush` on every component.
|
|
31
|
+
- **Smart (container) vs Dumb (presentational)** separation — mandatory.
|
|
32
|
+
- Smart: fetches data, owns state, dispatches NgRx actions or calls services.
|
|
33
|
+
- Dumb: receives data via `@Input()`, emits via `@Output()`, zero HTTP/store calls.
|
|
34
|
+
- Inputs/outputs with `@Input()` / `@Output()`.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
@Component({
|
|
38
|
+
selector: 'app-user-list',
|
|
39
|
+
templateUrl: './user-list.component.html',
|
|
40
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
41
|
+
})
|
|
42
|
+
export class UserListComponent implements OnInit, OnDestroy {
|
|
43
|
+
users: User[] = [];
|
|
44
|
+
isLoading = false;
|
|
45
|
+
error: string | null = null;
|
|
46
|
+
private destroy$ = new Subject<void>();
|
|
47
|
+
|
|
48
|
+
constructor(private userService: UserService) {}
|
|
49
|
+
|
|
50
|
+
ngOnInit(): void { this.loadUsers(); }
|
|
51
|
+
ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Template Syntax — Structural Directives
|
|
56
|
+
- Use `*ngIf`, `*ngFor` (with `trackBy`), `[ngSwitch]`.
|
|
57
|
+
- Built-in `@if/@for` does NOT exist (v17+ only).
|
|
58
|
+
- Always use `trackBy` with `*ngFor`.
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<app-spinner *ngIf="isLoading"></app-spinner>
|
|
62
|
+
<p *ngIf="error" class="error">{{ error }}</p>
|
|
63
|
+
<app-user-card
|
|
64
|
+
*ngFor="let user of users; trackBy: trackById"
|
|
65
|
+
[user]="user"
|
|
66
|
+
(delete)="onDelete($event)">
|
|
67
|
+
</app-user-card>
|
|
68
|
+
<p *ngIf="!isLoading && users.length === 0">No data.</p>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Services & Dependency Injection
|
|
72
|
+
- **Constructor injection only.** `inject()` function does NOT exist in v12.
|
|
73
|
+
- `@Injectable({ providedIn: 'root' })` for singleton services.
|
|
74
|
+
- HttpClient lives ONLY in services — never call HTTP from a component.
|
|
75
|
+
- Services return `Observable<T>` and NEVER subscribe internally.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
@Injectable({ providedIn: 'root' })
|
|
79
|
+
export class UserService {
|
|
80
|
+
private baseUrl = `${environment.apiUrl}/users`;
|
|
81
|
+
constructor(private http: HttpClient) {}
|
|
82
|
+
|
|
83
|
+
getAll(params?: UserFilterParams): Observable<ApiResponse<User[]>> {
|
|
84
|
+
return this.http.get<ApiResponse<User[]>>(this.baseUrl, { params: { ...params } });
|
|
85
|
+
}
|
|
86
|
+
getById(id: number): Observable<ApiResponse<User>> {
|
|
87
|
+
return this.http.get<ApiResponse<User>>(`${this.baseUrl}/${id}`);
|
|
88
|
+
}
|
|
89
|
+
create(payload: CreateUserRequest): Observable<ApiResponse<User>> {
|
|
90
|
+
return this.http.post<ApiResponse<User>>(this.baseUrl, payload);
|
|
91
|
+
}
|
|
92
|
+
update(id: number, payload: UpdateUserRequest): Observable<ApiResponse<User>> {
|
|
93
|
+
return this.http.put<ApiResponse<User>>(`${this.baseUrl}/${id}`, payload);
|
|
94
|
+
}
|
|
95
|
+
delete(id: number): Observable<ApiResponse<void>> {
|
|
96
|
+
return this.http.delete<ApiResponse<void>>(`${this.baseUrl}/${id}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## HTTP
|
|
102
|
+
- `HttpClientModule` imported in `CoreModule` / `AppModule`.
|
|
103
|
+
- Class-based interceptors registered via `HTTP_INTERCEPTORS` multi-provider in `CoreModule`.
|
|
104
|
+
- Auth interceptor attaches `Authorization: Bearer <token>`.
|
|
105
|
+
- Error interceptor handles 401 (refresh/redirect) and maps errors to user messages.
|
|
106
|
+
- Avoid `toPromise()` (deprecated) — use `firstValueFrom()` / `lastValueFrom()`.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
@Injectable()
|
|
110
|
+
export class AuthInterceptor implements HttpInterceptor {
|
|
111
|
+
constructor(private auth: AuthService) {}
|
|
112
|
+
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
|
113
|
+
const token = this.auth.accessToken;
|
|
114
|
+
return token
|
|
115
|
+
? next.handle(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }))
|
|
116
|
+
: next.handle(req);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## State Management — NgRx (common in v12 projects)
|
|
122
|
+
- If the project uses NgRx: Actions → Reducer → Effects → Selectors pattern.
|
|
123
|
+
- Smart components dispatch actions and select from store — NEVER call service directly when NgRx manages that slice.
|
|
124
|
+
- Effects own all side effects (HTTP calls, navigation).
|
|
125
|
+
- Selectors are the ONLY way to read from the store in components.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Smart component with NgRx
|
|
129
|
+
export class UserListComponent implements OnInit, OnDestroy {
|
|
130
|
+
users$ = this.store.select(selectUsers);
|
|
131
|
+
isLoading$ = this.store.select(selectUsersLoading);
|
|
132
|
+
private destroy$ = new Subject<void>();
|
|
133
|
+
|
|
134
|
+
constructor(private store: Store) {}
|
|
135
|
+
ngOnInit(): void { this.store.dispatch(UserActions.loadUsers()); }
|
|
136
|
+
ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); }
|
|
137
|
+
onDelete(id: number): void { this.store.dispatch(UserActions.deleteUser({ id })); }
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- If the project does NOT use NgRx: `BehaviorSubject` in a service for shared state, local state in component fields.
|
|
142
|
+
|
|
143
|
+
## Routing
|
|
144
|
+
- Lazy-load feature modules with `loadChildren: () => import('...').then(m => m.UserModule)`.
|
|
145
|
+
- Class-based guards implementing `CanActivate`, `CanLoad`.
|
|
146
|
+
|
|
147
|
+
## Reactive / RxJS (v6.x)
|
|
148
|
+
- No Signals (v16+ only). State is component fields or `BehaviorSubject`.
|
|
149
|
+
- Tear down ALL subscriptions with `takeUntil(this.destroy$)` + `ngOnDestroy`.
|
|
150
|
+
- Prefer the `async` pipe in templates over manual `subscribe` where possible.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
private loadUsers(): void {
|
|
154
|
+
this.isLoading = true;
|
|
155
|
+
this.userService.getAll()
|
|
156
|
+
.pipe(takeUntil(this.destroy$))
|
|
157
|
+
.subscribe({
|
|
158
|
+
next: (res) => { this.users = res.data; this.isLoading = false; },
|
|
159
|
+
error: () => { this.error = 'Could not load users'; this.isLoading = false; },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Forms
|
|
165
|
+
- Reactive Forms (`FormBuilder`) for all forms with validation — never Template-driven for complex forms.
|
|
166
|
+
- Show validation messages when a control is `touched` and `invalid`.
|
|
167
|
+
- Disable submit button while request is pending.
|
|
168
|
+
|
|
169
|
+
## TypeScript
|
|
170
|
+
- No `any` — use proper `interface` / `type`.
|
|
171
|
+
- Mirror backend DTOs in `*.model.ts`.
|
|
172
|
+
- `readonly` for arrays/objects that must not be mutated.
|
|
173
|
+
|
|
174
|
+
## Testing
|
|
175
|
+
- Unit (service): `HttpClientTestingModule` + `HttpTestingController`.
|
|
176
|
+
- Component: `TestBed.configureTestingModule` + mocked service (`jasmine.createSpyObj`).
|
|
177
|
+
- E2E: Protractor (default in v12, EOL) or Cypress/Playwright if the project migrated.
|
|
178
|
+
- Runner: Karma + Jasmine (default in v12).
|
|
179
|
+
|
|
180
|
+
## Security
|
|
181
|
+
- Tokens in memory (service field) or httpOnly cookie — NOT localStorage.
|
|
182
|
+
- No hardcoded backend URL — use `environment.apiUrl`.
|
|
183
|
+
- Guard routes with `CanActivate`; use `CanLoad` to prevent lazy module download.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Key Differences vs v14-15
|
|
188
|
+
- No standalone components — NgModule only.
|
|
189
|
+
- No `inject()` function — constructor injection only.
|
|
190
|
+
- No `takeUntilDestroyed` — use the `Subject + takeUntil + ngOnDestroy` pattern.
|
|
191
|
+
- No required `@Input()` decorator — use `!` assertion or explicit check.
|
|
192
|
+
- RxJS 6.x (pipeable operators, `of`, `from`, etc. — same as later versions).
|
|
193
|
+
- NgRx v12 — `createAction`, `createReducer`, `createEffect` patterns are standard.
|