From c4ab013689058f87b7b1230eddc7107afb13f801 Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Tue, 7 Oct 2025 17:23:14 +0300 Subject: [PATCH 1/7] Add AI config schematic for Angular projects Introduces a new 'ai-config' schematic that generates configuration files for various AI tools (Claude, Copilot, Cursor, Gemini, Junie, Windsurf) to guide code generation and best practices in Angular and ABP Framework projects. Includes schematic implementation, schema, templates for each tool, and updates to collection.json and build scripts. --- .../packages/schematics/src/collection.json | 6 +- .../ai-config/files/claude/.claude/CLAUDE.md | 106 +++ .../copilot/.github/copilot-instructions.md | 158 +++++ .../files/cursor/.cursor/rules/cursor.mdc | 271 ++++++++ .../ai-config/files/gemini/.gemini/GEMINI.md | 240 +++++++ .../files/junie/.junie/guidelines.md | 352 ++++++++++ .../windsurf/.windsurf/rules/guidelines.md | 652 ++++++++++++++++++ .../src/commands/ai-config/index.ts | 104 +++ .../src/commands/ai-config/model.ts | 12 + .../src/commands/ai-config/schema.json | 44 ++ npm/ng-packs/scripts/build-schematics.ts | 2 + 11 files changed, 1946 insertions(+), 1 deletion(-) create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts create mode 100644 npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json diff --git a/npm/ng-packs/packages/schematics/src/collection.json b/npm/ng-packs/packages/schematics/src/collection.json index 16f148d78a..c40d7dac14 100644 --- a/npm/ng-packs/packages/schematics/src/collection.json +++ b/npm/ng-packs/packages/schematics/src/collection.json @@ -34,7 +34,11 @@ "description": "ABP Change Styles of Theme Schematics", "factory": "./commands/change-theme", "schema": "./commands/change-theme/schema.json" - + }, + "ai-config": { + "description": "Generates AI configuration files for Angular projects", + "factory": "./commands/ai-config", + "schema": "./commands/ai-config/schema.json" } } } diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md new file mode 100644 index 0000000000..66c1a515ee --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md @@ -0,0 +1,106 @@ +# Angular & ABP Framework Development Rules for Claude + +## Project Context +This is an Angular application built with the ABP Framework. Follow these rules to generate high-quality, maintainable code. + +## Angular Best Practices + +### Component Development +- Use OnPush change detection strategy by default +- Implement OnDestroy and unsubscribe from observables +- Keep components focused on presentation logic +- Use smart/dumb component pattern +- Prefer standalone components in new code +- Use proper TypeScript typing, avoid `any` + +### Service Development +- Make services injectable with `providedIn: 'root'` when possible +- Use dependency injection properly +- Handle errors appropriately with RxJS operators +- Return observables for async operations +- Keep services focused on single responsibility + +### RxJS Best Practices +- Use proper operators: `switchMap`, `mergeMap`, `concatMap`, `exhaustMap` +- Always unsubscribe using `takeUntil`, `take`, or async pipe +- Avoid nested subscriptions +- Use `shareReplay` for shared streams +- Handle errors with `catchError` + +### Template Best Practices +- Use async pipe for observables +- Avoid complex logic in templates +- Use trackBy with *ngFor +- Use proper change detection +- Follow accessibility guidelines (ARIA attributes) + +## ABP Framework Specific Rules + +### Module Structure +- Follow ABP's modular architecture +- Use feature modules appropriately +- Leverage ABP's configuration services +- Use ABP's localization system + +### State Management +- Use ABP's state management patterns +- Leverage NGXS for complex state +- Use ABP's store decorators properly + +### API Integration +- Use ABP's generated proxy services +- Follow ABP's REST API conventions +- Handle ABP's error responses +- Use ABP's permission system + +### Localization +- Use ABP's localization pipes and services +- Define localization keys in resource files +- Follow ABP's localization naming conventions + +### Authentication & Authorization +- Use ABP's auth guards +- Leverage permission directives +- Handle ABP's multi-tenancy + +## Code Style +- Follow Angular style guide +- Use meaningful variable and function names +- Add JSDoc comments for complex logic +- Keep functions small and focused +- Use TypeScript strict mode +- Format code with Prettier + +## Testing +- Write unit tests for services and components +- Use Jest for testing +- Mock dependencies properly +- Aim for good test coverage +- Test error scenarios + +## File Organization +- Follow Nx workspace conventions +- Use proper folder structure +- Group related files together +- Use barrel exports (index.ts) + +## Performance +- Lazy load feature modules +- Use OnPush change detection +- Optimize bundle size +- Use production builds +- Implement proper caching strategies + +## Security +- Sanitize user inputs +- Use proper Content Security Policy +- Follow OWASP guidelines +- Validate data on client and server + +## Git Practices +- Write meaningful commit messages +- Keep commits atomic +- Follow conventional commits +- Create feature branches + +When generating code, always consider these rules and the context of the ABP Framework and Angular ecosystem. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md new file mode 100644 index 0000000000..f39facbae6 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md @@ -0,0 +1,158 @@ +# GitHub Copilot Instructions for Angular & ABP Framework + +You are an expert Angular and ABP Framework developer. Follow these guidelines when generating code suggestions. + +## Angular Development Standards + +### Components +- Create components with OnPush change detection strategy +- Implement lifecycle hooks properly (OnInit, OnDestroy) +- Use standalone components for new features +- Follow smart/dumb component pattern +- Unsubscribe from observables using takeUntil pattern or async pipe + +Example: +```typescript +@Component({ + selector: 'app-example', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, ReactiveFormsModule] +}) +export class ExampleComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### Services +- Use providedIn: 'root' for singleton services +- Return Observables for async operations +- Handle errors with proper RxJS operators +- Keep services focused on single responsibility + +Example: +```typescript +@Injectable({ providedIn: 'root' }) +export class DataService { + constructor(private http: HttpClient) {} + + getData(): Observable { + return this.http.get('/api/data').pipe( + catchError(this.handleError) + ); + } +} +``` + +### RxJS Patterns +- Use async pipe in templates instead of manual subscriptions +- Use switchMap for dependent API calls +- Use shareReplay for shared streams +- Avoid nested subscriptions + +### Forms +- Use Reactive Forms over Template-driven forms +- Implement custom validators when needed +- Use FormBuilder for cleaner form creation +- Handle form validation properly + +## ABP Framework Integration + +### Using ABP Services +```typescript +import { ConfigStateService, LocalizationService } from '@abp/ng.core'; + +constructor( + private config: ConfigStateService, + private localization: LocalizationService +) {} +``` + +### Localization +```typescript +// In component +this.localization.instant('::LocalizationKey') + +// In template +{{ '::LocalizationKey' | abpLocalization }} +``` + +### Permissions +```typescript +// In template + + +// In component +if (this.config.getGrantedPolicy('MyApp.MyPermission')) { + // do something +} +``` + +### API Proxy Integration +- Use ABP's generated proxy services +- Don't create manual HTTP calls for ABP APIs +- Follow ABP's DTOs and service patterns + +### State Management with NGXS +```typescript +@State({ + name: 'MyState', + defaults: { items: [] } +}) +@Injectable() +export class MyState { + @Action(GetItems) + getItems(ctx: StateContext) { + return this.service.getItems().pipe( + tap(items => ctx.patchState({ items })) + ); + } +} +``` + +## Code Quality Standards +- Use TypeScript strict mode +- Avoid using `any` type +- Implement proper error handling +- Write meaningful variable and function names +- Add comments for complex logic +- Follow SOLID principles + +## Testing +- Write unit tests for components and services +- Use Jest testing framework +- Mock dependencies properly +- Test both success and error scenarios + +## File Structure +Follow Nx workspace structure: +``` +libs/ + feature-name/ + src/ + lib/ + components/ + services/ + models/ + state/ +``` + +## Performance +- Use OnPush change detection +- Lazy load feature modules +- Use trackBy in *ngFor +- Optimize bundle size +- Avoid memory leaks + +## Security +- Never commit sensitive data +- Sanitize user inputs +- Use proper authentication guards +- Follow ABP's security patterns + +Always prioritize code maintainability, readability, and following Angular and ABP best practices. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc new file mode 100644 index 0000000000..fbcf65e2fa --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc @@ -0,0 +1,271 @@ +# Cursor AI Rules for Angular & ABP Framework Development + +## Core Principles +- Write clean, maintainable, and testable code +- Follow Angular style guide and ABP conventions +- Use TypeScript strict mode +- Prioritize code readability over cleverness + +## Angular Component Guidelines + +### Component Structure +```typescript +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Subject, takeUntil } from 'rxjs'; + +@Component({ + selector: 'app-feature-name', + standalone: true, + imports: [CommonModule], + templateUrl: './feature-name.component.html', + styleUrls: ['./feature-name.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FeatureNameComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); + + ngOnInit(): void { + // Initialization logic + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### Always: +- Use OnPush change detection strategy +- Implement OnDestroy for cleanup +- Use standalone components for new code +- Type everything properly, avoid `any` +- Use async pipe in templates + +### Never: +- Mutate state directly +- Forget to unsubscribe from observables +- Use nested subscriptions +- Put business logic in components + +## Service Patterns + +```typescript +import { Injectable } from '@angular/core'; +import { Observable, catchError, map, shareReplay } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; + +@Injectable({ providedIn: 'root' }) +export class DataService { + private cache$ = new Map>(); + + constructor(private http: HttpClient) {} + + getData(id: string): Observable { + if (!this.cache$.has(id)) { + this.cache$.set( + id, + this.http.get(`/api/data/${id}`).pipe( + shareReplay(1), + catchError(this.handleError) + ) + ); + } + return this.cache$.get(id)!; + } + + private handleError(error: any): Observable { + console.error('An error occurred:', error); + throw error; + } +} +``` + +## RxJS Best Practices + +### Use Proper Operators +- `switchMap`: Cancel previous request (search, navigation) +- `mergeMap`: Parallel requests (batch operations) +- `concatMap`: Sequential requests (ordered operations) +- `exhaustMap`: Ignore new requests until current completes (form submit) + +### Memory Management +```typescript +// Good: Using takeUntil +this.dataService.getData() + .pipe(takeUntil(this.destroy$)) + .subscribe(data => this.data = data); + +// Better: Using async pipe +data$ = this.dataService.getData(); +// Template: {{ data$ | async }} +``` + +## ABP Framework Integration + +### Service Injection +```typescript +import { + ConfigStateService, + LocalizationService, + PermissionService +} from '@abp/ng.core'; + +constructor( + private config: ConfigStateService, + private localization: LocalizationService, + private permission: PermissionService +) {} +``` + +### Localization Usage +```typescript +// Component +readonly localizationKeys = { + title: this.localization.instant('::PageTitle'), + save: this.localization.instant('::Save'), + cancel: this.localization.instant('::Cancel') +}; + +// Template +

{{ '::PageTitle' | abpLocalization }}

+``` + +### Permission Checks +```typescript +// Template + + +// Component +canCreate$ = this.config.getGrantedPolicy$('MyApp.Users.Create'); +``` + +### Using ABP Proxy Services +```typescript +import { UserService } from '@proxy/users'; + +constructor(private userService: UserService) {} + +ngOnInit(): void { + this.users$ = this.userService.getList({ maxResultCount: 10 }); +} +``` + +## State Management (NGXS) + +```typescript +import { State, Action, StateContext, Selector } from '@ngxs/store'; +import { tap } from 'rxjs'; + +export class GetUsers { + static readonly type = '[Users] Get Users'; +} + +export interface UsersStateModel { + users: User[]; + loading: boolean; +} + +@State({ + name: 'users', + defaults: { + users: [], + loading: false + } +}) +@Injectable() +export class UsersState { + constructor(private userService: UserService) {} + + @Selector() + static getUsers(state: UsersStateModel) { + return state.users; + } + + @Action(GetUsers) + getUsers(ctx: StateContext) { + ctx.patchState({ loading: true }); + return this.userService.getList().pipe( + tap(response => { + ctx.patchState({ + users: response.items, + loading: false + }); + }) + ); + } +} +``` + +## Form Handling + +```typescript +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +export class FormComponent implements OnInit { + form!: FormGroup; + + constructor(private fb: FormBuilder) {} + + ngOnInit(): void { + this.form = this.fb.group({ + name: ['', [Validators.required, Validators.minLength(3)]], + email: ['', [Validators.required, Validators.email]], + age: [null, [Validators.min(18), Validators.max(100)]] + }); + } + + onSubmit(): void { + if (this.form.valid) { + const formValue = this.form.getRawValue(); + // Submit logic + } + } +} +``` + +## Template Best Practices + +```html + +
+ {{ data.name }} +
+ + +
+ {{ item.name }} +
+ + + +``` + +## Common Patterns to Avoid +❌ Manual subscriptions without cleanup +❌ Logic in templates +❌ Mutating input properties +❌ Using `any` type +❌ Nested subscriptions +❌ Missing error handling + +## Common Patterns to Use +✅ Async pipe in templates +✅ OnPush change detection +✅ Reactive forms +✅ Smart/dumb components +✅ Dependency injection +✅ RxJS operators +✅ Proper typing + +Follow these rules consistently to maintain high code quality. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md new file mode 100644 index 0000000000..83e598bfb0 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md @@ -0,0 +1,240 @@ +# Gemini AI - Angular & ABP Framework Development Guidelines + +## Project Overview +This is an enterprise Angular application using the ABP Framework with Nx workspace structure and NGXS for state management. + +## Angular Development Standards + +### Component Architecture +Always create components with: +- OnPush change detection strategy +- Proper lifecycle hook implementation (OnDestroy for cleanup) +- Standalone components for new features +- TypeScript strict typing (avoid `any`) + +```typescript +@Component({ + selector: 'app-example', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, ReactiveFormsModule] +}) +export class ExampleComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### Service Development +- Use `providedIn: 'root'` for singleton services +- Return Observables for async operations +- Implement proper error handling with RxJS operators +- Use dependency injection properly + +```typescript +@Injectable({ providedIn: 'root' }) +export class DataService { + constructor(private http: HttpClient) {} + + getData(): Observable { + return this.http.get('/api/data').pipe( + retry(2), + catchError(this.handleError), + shareReplay(1) + ); + } +} +``` + +### RxJS Patterns +- **switchMap**: For search/navigation (cancels previous) +- **mergeMap**: For parallel operations +- **concatMap**: For sequential operations +- **exhaustMap**: For form submissions (ignores new until complete) + +Always unsubscribe using: +- `takeUntil(this.destroy$)` pattern +- `async` pipe in templates (preferred) +- `take(1)` for single emissions + +### Template Best Practices +```html + +
+
+ {{ item.name }} +
+
+ + +``` + +## ABP Framework Integration + +### Localization +```typescript +// Component +this.localization.instant('::LocalizationKey') + +// Template +{{ '::WelcomeMessage' | abpLocalization }} +``` + +### Permissions +```typescript +// Template + + +// Component +canEdit$ = this.config.getGrantedPolicy$('MyApp.Books.Edit'); +``` + +### API Proxy Services +Always use ABP's generated proxy services instead of manual HTTP calls: + +```typescript +import { BookService } from '@proxy/books'; + +constructor(private bookService: BookService) {} + +getBooks(): Observable> { + return this.bookService.getList({ maxResultCount: 10 }); +} +``` + +### State Management (NGXS) +```typescript +@State({ + name: 'books', + defaults: { books: [], loading: false } +}) +@Injectable() +export class BooksState { + @Selector() + static getBooks(state: BooksStateModel) { + return state.books; + } + + @Action(GetBooks) + getBooks(ctx: StateContext) { + ctx.patchState({ loading: true }); + return this.bookService.getList().pipe( + tap(response => { + ctx.patchState({ + books: response.items, + loading: false + }); + }) + ); + } +} +``` + +## Code Quality Standards + +### TypeScript +- Use strict mode +- Avoid `any` type +- Use interfaces and types properly +- Implement proper null checks + +### Testing +- Write unit tests with Jest +- Mock dependencies properly +- Test both success and error scenarios +- Aim for good coverage + +### Performance +- Use OnPush change detection +- Lazy load feature modules +- Implement trackBy for lists +- Use production builds +- Avoid memory leaks + +### Security +- Sanitize user inputs +- Use Angular's built-in XSS protection +- Validate on client and server +- Follow ABP's security patterns +- Never expose sensitive data + +## File Structure (Nx Workspace) +``` +libs/ + feature-name/ + src/ + lib/ + components/ + services/ + models/ + state/ + guards/ +``` + +## Common Patterns + +### Smart/Dumb Components +```typescript +// Smart (Container) +@Component({ + template: ` + + + ` +}) +export class ContainerComponent { + items$ = this.store.select(getItems); + constructor(private store: Store) {} +} + +// Dumb (Presentational) +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ListComponent { + @Input() items: Item[] = []; + @Output() itemSelected = new EventEmitter(); +} +``` + +### Reactive Forms +```typescript +form = this.fb.group({ + name: ['', [Validators.required, Validators.minLength(3)]], + email: ['', [Validators.required, Validators.email]] +}); +``` + +## Best Practices Checklist +✅ OnPush change detection +✅ Proper unsubscription +✅ Async pipe in templates +✅ TypeScript strict typing +✅ Error handling +✅ Unit tests +✅ Localization (no hardcoded strings) +✅ Permission checks +✅ Accessibility attributes +✅ Performance optimization + +## Anti-Patterns to Avoid +❌ Using `any` type +❌ Manual subscriptions without cleanup +❌ Logic in templates +❌ Nested subscriptions +❌ Mutating state directly +❌ Missing error handling +❌ Hardcoded strings + +## Resources +- Angular Style Guide: https://angular.io/guide/styleguide +- ABP Documentation: https://docs.abp.io +- RxJS Operators: https://rxjs.dev/guide/operators + +Always prioritize code maintainability, readability, and following Angular and ABP Framework best practices. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md new file mode 100644 index 0000000000..f7656afa38 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md @@ -0,0 +1,352 @@ +# Junie AI Guidelines - Angular & ABP Framework + +## Introduction +You are assisting with an Angular application built on the ABP Framework. Follow these guidelines to generate high-quality, maintainable code that adheres to best practices. + +## Core Principles +1. **Type Safety**: Use TypeScript strict mode, avoid `any` +2. **Performance**: OnPush change detection, lazy loading +3. **Maintainability**: Clean, readable, well-documented code +4. **Security**: Input validation, proper authentication/authorization +5. **Accessibility**: WCAG 2.1 compliance, ARIA attributes + +## Angular Component Guidelines + +### Component Structure +```typescript +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Subject, takeUntil } from 'rxjs'; + +@Component({ + selector: 'app-feature', + standalone: true, + imports: [CommonModule], + templateUrl: './feature.component.html', + styleUrls: ['./feature.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FeatureComponent implements OnInit, OnDestroy { + private readonly destroy$ = new Subject(); + + ngOnInit(): void { + // Initialization + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### Key Requirements +- ✅ Use OnPush change detection +- ✅ Implement OnDestroy for cleanup +- ✅ Prefer standalone components +- ✅ Use proper TypeScript types +- ✅ Follow smart/dumb component pattern + +## Service Development + +```typescript +@Injectable({ providedIn: 'root' }) +export class DataService { + constructor(private http: HttpClient) {} + + getData(): Observable { + return this.http.get('/api/data').pipe( + retry(2), + catchError(this.handleError), + shareReplay(1) + ); + } + + private handleError(error: HttpErrorResponse): Observable { + console.error('Service error:', error); + return throwError(() => new Error('Operation failed')); + } +} +``` + +## RxJS Best Practices + +### Operator Selection +| Operator | Use Case | Example | +|----------|----------|---------| +| `switchMap` | Search, navigation (cancel previous) | Search input | +| `mergeMap` | Parallel operations | Batch API calls | +| `concatMap` | Sequential operations | Ordered processing | +| `exhaustMap` | Ignore until complete | Form submission | + +### Subscription Management +```typescript +// ✅ BEST: Use async pipe +data$ = this.service.getData(); + +// ✅ GOOD: Use takeUntil +this.service.getData() + .pipe(takeUntil(this.destroy$)) + .subscribe(data => this.handleData(data)); + +// ❌ BAD: No unsubscription +this.service.getData().subscribe(data => this.data = data); +``` + +## ABP Framework Integration + +### Localization +```typescript +// Service injection +constructor(private localization: LocalizationService) {} + +// Usage in component +getTranslation(key: string): string { + return this.localization.instant(`::${key}`); +} + +// Template usage +{{ '::PageTitle' | abpLocalization }} +``` + +### Permission System +```typescript +// Directive in template + + +// Check in component +canEdit(): boolean { + return this.config.getGrantedPolicy('MyApp.Books.Edit'); +} + +// Observable permission +canEdit$ = this.config.getGrantedPolicy$('MyApp.Books.Edit'); +``` + +### API Proxy Services +```typescript +// ✅ DO: Use generated proxy +import { BookService } from '@proxy/books'; + +constructor(private bookService: BookService) {} + +loadBooks(): void { + this.books$ = this.bookService.getList({ maxResultCount: 10 }); +} + +// ❌ DON'T: Manual HTTP calls for ABP APIs +``` + +### State Management (NGXS) +```typescript +// Actions +export class LoadBooks { + static readonly type = '[Books] Load Books'; +} + +// State +@State({ + name: 'books', + defaults: { books: [], loading: false } +}) +@Injectable() +export class BooksState { + constructor(private bookService: BookService) {} + + @Selector() + static books(state: BooksStateModel) { + return state.books; + } + + @Action(LoadBooks) + loadBooks(ctx: StateContext) { + ctx.patchState({ loading: true }); + return this.bookService.getList().pipe( + tap(response => ctx.patchState({ + books: response.items, + loading: false + })) + ); + } +} +``` + +## Forms + +### Reactive Forms +```typescript +export class FormComponent implements OnInit { + form: FormGroup; + + constructor(private fb: FormBuilder) {} + + ngOnInit(): void { + this.form = this.fb.group({ + name: ['', [Validators.required, Validators.maxLength(100)]], + email: ['', [Validators.required, Validators.email]], + age: [null, [Validators.min(0), Validators.max(120)]] + }); + } + + onSubmit(): void { + if (this.form.valid) { + const formData = this.form.getRawValue(); + this.submitData(formData); + } + } +} +``` + +## Template Best Practices + +```html + +
+
+

{{ user.name }}

+

{{ user.email }}

+
+
+ + + + + +

{{ '::WelcomeMessage' | abpLocalization }}

+``` + +## Testing + +```typescript +describe('BookService', () => { + let service: BookService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [BookService] + }); + service = TestBed.inject(BookService); + httpMock = TestBed.inject(HttpTestingController); + }); + + it('should fetch books', () => { + const mockBooks = [{ id: 1, title: 'Test' }]; + + service.getList().subscribe(books => { + expect(books).toEqual(mockBooks); + }); + + const req = httpMock.expectOne('/api/app/books'); + req.flush(mockBooks); + }); +}); +``` + +## Performance Optimization + +### Change Detection +- Use OnPush strategy everywhere possible +- Avoid function calls in templates +- Use pure pipes +- Implement trackBy for lists + +### Lazy Loading +```typescript +const routes: Routes = [ + { + path: 'books', + loadChildren: () => import('./books/books.module') + .then(m => m.BooksModule) + } +]; +``` + +### Bundle Optimization +- Use standalone components +- Implement lazy loading +- Use dynamic imports +- Tree-shake unused code + +## Security Best Practices + +1. **Input Validation**: Always validate user input +2. **Sanitization**: Use DomSanitizer when needed +3. **XSS Prevention**: Leverage Angular's built-in protection +4. **Authentication**: Use ABP's auth system +5. **Authorization**: Check permissions properly +6. **Data Protection**: Never expose sensitive data in client + +## Code Quality Checklist + +Before submitting code, ensure: +- [ ] TypeScript strict mode enabled +- [ ] No `any` types used +- [ ] OnPush change detection applied +- [ ] Proper unsubscription implemented +- [ ] Error handling in place +- [ ] Unit tests written +- [ ] Localization keys used (no hardcoded text) +- [ ] Permission checks added +- [ ] Accessibility attributes included +- [ ] Performance optimized + +## File Organization (Nx Workspace) + +``` +libs/ + feature-name/ + src/ + lib/ + components/ + component-name/ + component-name.component.ts + component-name.component.html + component-name.component.scss + component-name.component.spec.ts + services/ + models/ + state/ + guards/ + pipes/ + directives/ + index.ts (public API) +``` + +## Common Patterns + +### Smart/Dumb Components +- **Smart**: Container with business logic, state management +- **Dumb**: Presentational with @Input/@Output, OnPush + +### Service Layer +- **API Services**: Backend communication +- **Business Services**: Business logic +- **Utility Services**: Helper functions + +## Anti-Patterns to Avoid + +❌ Using `any` type +❌ Forgetting to unsubscribe +❌ Complex logic in templates +❌ Nested subscriptions +❌ Direct state mutation +❌ Missing error handling +❌ Hardcoded strings +❌ Skipping unit tests + +## Additional Resources + +- Angular Style Guide: https://angular.io/guide/styleguide +- ABP Framework Docs: https://docs.abp.io +- RxJS Documentation: https://rxjs.dev +- Nx Documentation: https://nx.dev +- NGXS Documentation: https://www.ngxs.io + +Follow these guidelines consistently to produce high-quality, maintainable Angular applications with ABP Framework. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md new file mode 100644 index 0000000000..7449f4f582 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md @@ -0,0 +1,652 @@ +# Windsurf AI Development Guidelines - Angular & ABP Framework + +## Project Context +This is an enterprise-grade Angular application built on the ABP Framework, using Nx for workspace management and NGXS for state management. Follow these comprehensive guidelines to generate production-ready code. + +--- + +## 🎯 Core Development Principles + +### 1. Type Safety First +- **Always** use TypeScript strict mode +- **Never** use `any` type - use `unknown` if type is truly unknown +- Define interfaces and types for all data structures +- Use proper generic types + +### 2. Performance Optimization +- Use OnPush change detection strategy by default +- Implement lazy loading for feature modules +- Use trackBy with *ngFor directives +- Leverage async pipe for observables +- Avoid memory leaks with proper cleanup + +### 3. Code Maintainability +- Follow SOLID principles +- Write self-documenting code with clear naming +- Keep functions small (<20 lines ideally) +- Add JSDoc comments for complex logic +- Use meaningful variable and function names + +### 4. Security +- Validate all user inputs +- Sanitize data when necessary (DomSanitizer) +- Use ABP's permission system +- Never expose sensitive data in client code +- Follow OWASP security guidelines + +### 5. Accessibility +- Include ARIA attributes +- Support keyboard navigation +- Use semantic HTML +- Follow WCAG 2.1 AA standards + +--- + +## 📦 Angular Component Architecture + +### Standard Component Structure + +```typescript +import { + ChangeDetectionStrategy, + Component, + OnDestroy, + OnInit, + inject +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Subject, takeUntil } from 'rxjs'; + +@Component({ + selector: 'app-feature-name', + standalone: true, + imports: [CommonModule], + templateUrl: './feature-name.component.html', + styleUrls: ['./feature-name.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FeatureNameComponent implements OnInit, OnDestroy { + // Use inject() function (Angular 14+) + private readonly dataService = inject(DataService); + private readonly destroy$ = new Subject(); + + // Observable streams with $ suffix + data$ = this.dataService.getData(); + + ngOnInit(): void { + // Initialization logic + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### Component Best Practices +✅ **DO:** +- Use OnPush change detection +- Implement OnDestroy for cleanup +- Use standalone components for new code +- Prefer async pipe over manual subscriptions +- Use readonly for immutable properties +- Use inject() function for dependency injection + +❌ **DON'T:** +- Put business logic in components +- Mutate @Input() properties +- Forget to unsubscribe from observables +- Use function calls in templates +- Use nested subscriptions + +--- + +## 🔧 Service Development + +### Service Pattern + +```typescript +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, catchError, retry, shareReplay, throwError } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class DataService { + private readonly http = inject(HttpClient); + private readonly cache$ = new Map>(); + + getData(id: string): Observable { + // Implement caching + if (!this.cache$.has(id)) { + this.cache$.set( + id, + this.http.get(`/api/data/${id}`).pipe( + retry(2), + catchError(this.handleError), + shareReplay(1) + ) + ); + } + return this.cache$.get(id)!; + } + + private handleError(error: HttpErrorResponse): Observable { + console.error('Service error:', error); + // Log to monitoring service here + return throwError(() => new Error('Operation failed. Please try again.')); + } +} +``` + +### Service Best Practices +- Use `providedIn: 'root'` for singleton services +- Return Observables for async operations +- Implement proper error handling +- Use caching strategies when appropriate +- Keep services focused (Single Responsibility) + +--- + +## 🌊 RxJS Patterns & Operators + +### Operator Decision Matrix + +| Operator | Use Case | Behavior | +|----------|----------|----------| +| **switchMap** | Search, navigation | Cancels previous, emits latest | +| **mergeMap** | Parallel operations | Runs all concurrently | +| **concatMap** | Sequential operations | Maintains order, waits for completion | +| **exhaustMap** | Form submission, clicks | Ignores new until current completes | + +### Subscription Management + +```typescript +export class ExampleComponent implements OnDestroy { + private readonly destroy$ = new Subject(); + + ngOnInit(): void { + // Pattern 1: takeUntil + this.service.getData() + .pipe(takeUntil(this.destroy$)) + .subscribe(data => this.handleData(data)); + + // Pattern 2: take(1) for single emission + this.service.getConfig() + .pipe(take(1)) + .subscribe(config => this.config = config); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### Template Usage (Preferred) + +```typescript +// Component +data$ = this.service.getData().pipe( + catchError(error => { + this.handleError(error); + return of([]); + }) +); + +// Template +
+ {{ data.name }} +
+``` + +--- + +## 🏗️ ABP Framework Integration + +### 1. Localization System + +```typescript +// Component +import { LocalizationService } from '@abp/ng.core'; + +export class MyComponent { + private readonly localization = inject(LocalizationService); + + readonly texts = { + title: this.localization.instant('::PageTitle'), + save: this.localization.instant('::Save'), + cancel: this.localization.instant('::Cancel') + }; +} + +// Template +

{{ '::PageTitle' | abpLocalization }}

+ +``` + +### 2. Permission System + +```typescript +// Template + + +// Component +import { ConfigStateService } from '@abp/ng.core'; + +export class BookListComponent { + private readonly config = inject(ConfigStateService); + + canEdit$ = this.config.getGrantedPolicy$('BookStore.Books.Edit'); + canDelete$ = this.config.getGrantedPolicy$('BookStore.Books.Delete'); + + checkPermission(): boolean { + return this.config.getGrantedPolicy('BookStore.Books.Create'); + } +} +``` + +### 3. API Proxy Services + +```typescript +// ✅ ALWAYS use generated proxy services +import { BookService } from '@proxy/books'; +import { GetBooksInput } from '@proxy/books/models'; + +export class BookListComponent { + private readonly bookService = inject(BookService); + + books$ = this.bookService.getList({ + maxResultCount: 10, + skipCount: 0 + }); + + createBook(input: CreateBookDto): void { + this.bookService.create(input).pipe( + take(1), + catchError(this.handleError) + ).subscribe(() => this.refreshList()); + } +} + +// ❌ DON'T create manual HTTP calls for ABP APIs +``` + +### 4. State Management with NGXS + +```typescript +// Actions +export class GetBooks { + static readonly type = '[Books] Get Books'; + constructor(public payload: GetBooksInput) {} +} + +export class CreateBook { + static readonly type = '[Books] Create Book'; + constructor(public payload: CreateBookDto) {} +} + +// State +export interface BooksStateModel { + books: BookDto[]; + loading: boolean; + error: string | null; + totalCount: number; +} + +@State({ + name: 'books', + defaults: { + books: [], + loading: false, + error: null, + totalCount: 0 + } +}) +@Injectable() +export class BooksState { + private readonly bookService = inject(BookService); + + @Selector() + static books(state: BooksStateModel): BookDto[] { + return state.books; + } + + @Selector() + static loading(state: BooksStateModel): boolean { + return state.loading; + } + + @Selector() + static totalCount(state: BooksStateModel): number { + return state.totalCount; + } + + @Action(GetBooks) + getBooks(ctx: StateContext, action: GetBooks) { + ctx.patchState({ loading: true, error: null }); + + return this.bookService.getList(action.payload).pipe( + tap(response => { + ctx.patchState({ + books: response.items, + totalCount: response.totalCount, + loading: false + }); + }), + catchError(error => { + ctx.patchState({ + loading: false, + error: error.message + }); + return throwError(() => error); + }) + ); + } + + @Action(CreateBook) + createBook(ctx: StateContext, action: CreateBook) { + return this.bookService.create(action.payload).pipe( + tap(book => { + const state = ctx.getState(); + ctx.patchState({ + books: [...state.books, book], + totalCount: state.totalCount + 1 + }); + }) + ); + } +} +``` + +### 5. Multi-Tenancy Support + +```typescript +import { ConfigStateService } from '@abp/ng.core'; + +export class TenantAwareComponent { + private readonly config = inject(ConfigStateService); + + get currentTenant() { + return this.config.getOne('currentTenant'); + } + + get isTenantContext(): boolean { + return !!this.currentTenant?.id; + } +} +``` + +--- + +## 📝 Reactive Forms + +### Form Implementation + +```typescript +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { CustomValidators } from './validators'; + +export class BookFormComponent implements OnInit { + private readonly fb = inject(FormBuilder); + + bookForm!: FormGroup; + + ngOnInit(): void { + this.bookForm = this.fb.group({ + name: ['', [ + Validators.required, + Validators.minLength(3), + Validators.maxLength(128) + ]], + type: ['', Validators.required], + publishDate: ['', [ + Validators.required, + CustomValidators.notFutureDate + ]], + price: [0, [ + Validators.required, + Validators.min(0), + Validators.max(999999.99) + ]], + description: ['', Validators.maxLength(1000)] + }); + } + + onSubmit(): void { + if (this.bookForm.valid) { + const formValue = this.bookForm.getRawValue(); + this.submitForm(formValue); + } else { + this.markFormGroupTouched(this.bookForm); + } + } + + private markFormGroupTouched(formGroup: FormGroup): void { + Object.keys(formGroup.controls).forEach(key => { + const control = formGroup.get(key); + control?.markAsTouched(); + + if (control instanceof FormGroup) { + this.markFormGroupTouched(control); + } + }); + } +} +``` + +--- + +## 🎨 Template Best Practices + +```html + +
+
+

{{ book.name }}

+

{{ book.publishDate | date:'shortDate' }}

+

{{ book.price | currency }}

+
+
+ + +
Loading...
+
+ + + + + +
+ +
+ + +

{{ '::BookManagement' | abpLocalization }}

+

{{ '::BookDescription' | abpLocalization:{ name: book.name } }}

+``` + +--- + +## 🧪 Testing Strategies + +### Component Testing + +```typescript +describe('BookListComponent', () => { + let component: BookListComponent; + let fixture: ComponentFixture; + let mockBookService: jasmine.SpyObj; + + beforeEach(async () => { + mockBookService = jasmine.createSpyObj('BookService', [ + 'getList', + 'create', + 'delete' + ]); + + await TestBed.configureTestingModule({ + imports: [BookListComponent], + providers: [ + { provide: BookService, useValue: mockBookService } + ] + }).compileComponents(); + + fixture = TestBed.createComponent(BookListComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should load books on init', () => { + const mockBooks = [ + { id: '1', name: 'Book 1', price: 10 }, + { id: '2', name: 'Book 2', price: 20 } + ]; + + mockBookService.getList.and.returnValue(of({ + items: mockBooks, + totalCount: 2 + })); + + component.ngOnInit(); + + expect(mockBookService.getList).toHaveBeenCalled(); + }); +}); +``` + +--- + +## 🚀 Performance Optimization + +### 1. Change Detection Strategy +```typescript +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush +}) +``` + +### 2. TrackBy Functions +```typescript +trackByBookId(index: number, book: BookDto): string { + return book.id; +} +``` + +### 3. Lazy Loading +```typescript +const routes: Routes = [ + { + path: 'books', + loadChildren: () => import('./books/books.routes') + .then(m => m.BOOKS_ROUTES) + } +]; +``` + +### 4. Virtual Scrolling (for large lists) +```typescript + +
+ {{ book.name }} +
+
+``` + +--- + +## 📁 File Structure (Nx Workspace) + +``` +libs/ + books/ + feature/ + src/ + lib/ + components/ + book-list/ + book-form/ + book-detail/ + services/ + state/ + guards/ + books-feature.routes.ts + index.ts + data-access/ + src/ + lib/ + services/ + models/ + index.ts + ui/ + src/ + lib/ + components/ + index.ts +``` + +--- + +## ✅ Quality Checklist + +Before committing code, verify: +- [ ] TypeScript strict mode compliance +- [ ] No `any` types +- [ ] OnPush change detection +- [ ] Proper unsubscription +- [ ] Error handling implemented +- [ ] Unit tests written +- [ ] Localization keys used +- [ ] Permission checks added +- [ ] Accessibility attributes included +- [ ] Performance optimized (trackBy, lazy loading) +- [ ] Security validated +- [ ] Code formatted (Prettier) +- [ ] Linting passed + +--- + +## 🚫 Common Anti-Patterns + +| Anti-Pattern | Why It's Bad | Better Approach | +|--------------|--------------|-----------------| +| Using `any` | Loses type safety | Use proper types or `unknown` | +| No unsubscribe | Memory leaks | Use takeUntil or async pipe | +| Logic in templates | Hard to test | Move to component/service | +| Nested subscriptions | Hard to maintain | Use RxJS operators | +| Direct state mutation | Breaks change detection | Use immutable patterns | +| Missing error handling | Poor UX | Always handle errors | +| Hardcoded strings | Not localizable | Use localization system | + +--- + +## 📚 Resources + +- [Angular Style Guide](https://angular.io/guide/styleguide) +- [ABP Framework Documentation](https://docs.abp.io) +- [RxJS Documentation](https://rxjs.dev) +- [Nx Documentation](https://nx.dev) +- [NGXS Documentation](https://www.ngxs.io) + +--- + +Follow these guidelines to build maintainable, performant, and secure Angular applications with ABP Framework. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts new file mode 100644 index 0000000000..10691f6609 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts @@ -0,0 +1,104 @@ +import { Rule, SchematicsException, Tree, apply, url, mergeWith, MergeStrategy, filter, chain } from '@angular-devkit/schematics'; +import { join, normalize } from '@angular-devkit/core'; +import { AiConfigSchema, AiTool } from './model'; +import { getWorkspace } from '../../utils'; + +/** + * Generates AI configuration files for Angular projects based on selected tools. + * This schematic creates configuration files that help AI tools follow Angular best practices. + */ +export default function (options: AiConfigSchema): Rule { + return async (tree: Tree) => { + // Validate options + if (!options.tool || options.tool.length === 0) { + console.log('ℹ️ No AI tools selected. Skipping configuration generation.'); + return tree; + } + + // Get workspace configuration + const workspace = await getWorkspace(tree); + let targetPath = '/'; + + // If targetProject is specified, generate in project directory + if (options.targetProject) { + const project = workspace.projects.get(options.targetProject); + if (!project) { + throw new SchematicsException( + `Project "${options.targetProject}" not found in workspace.` + ); + } + targetPath = normalize(project.root); + } + + console.log('🚀 Generating AI configuration files...'); + console.log(`📁 Target path: ${targetPath}`); + console.log(`🤖 Selected tools: ${options.tool.join(', ')}`); + + // Generate rules for each selected tool + const rules: Rule[] = options.tool + .map(tool => generateConfigForTool(tool, targetPath, options.overwrite || false)); + + // Apply all rules and log results + return chain([ + ...rules, + (tree: Tree) => { + console.log('✅ AI configuration files generated successfully!'); + console.log('\n📝 Generated files:'); + + options.tool.forEach(tool => { + const configPath = getConfigPath(tool, targetPath); + console.log(` - ${configPath}`); + }); + + console.log('\n💡 Tip: Restart your IDE or AI tool to apply the new configurations.'); + + return tree; + } + ]); + }; +} + +/** + * Generates configuration for a specific AI tool + */ +function generateConfigForTool(tool: AiTool, targetPath: string, overwrite: boolean): Rule { + return (tree: Tree) => { + const configPath = getConfigPath(tool, targetPath); + + // Check if file already exists + if (tree.exists(configPath) && !overwrite) { + console.log(`⚠️ Configuration file already exists: ${configPath}`); + console.log(` Use --overwrite flag to replace existing files.`); + return tree; + } + + // Get template source + const sourceDir = `./files/${tool}`; + const source = apply(url(sourceDir), [ + filter(path => { + // Filter out any unnecessary files + return !path.endsWith('.DS_Store'); + }) + ]); + + // Merge with existing tree + return mergeWith(source, overwrite ? MergeStrategy.Overwrite : MergeStrategy.Default); + }; +} + +/** + * Gets the configuration file path for a specific tool + */ +function getConfigPath(tool: AiTool, basePath: string): string { + const configFiles: Record = { + claude: '.claude/CLAUDE.md', + copilot: '.github/copilot-instructions.md', + cursor: '.cursor/rules/cursor.mdc', + gemini: '.gemini/GEMINI.md', + junie: '.junie/guidelines.md', + windsurf: '.windsurf/rules/guidelines.md' + }; + + const configFile = configFiles[tool]; + return join(normalize(basePath), configFile); +} diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts new file mode 100644 index 0000000000..8a10f85509 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts @@ -0,0 +1,12 @@ +export type AiTool = 'claude' | 'copilot' | 'cursor' | 'gemini' | 'junie' | 'windsurf'; + +export interface AiConfigSchema { + tool: AiTool[]; + targetProject?: string; + overwrite?: boolean; +} + +export interface AiConfigFile { + path: string; + content: string; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json b/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json new file mode 100644 index 0000000000..7ad18a1fe4 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "SchematicsABPAiConfig", + "title": "ABP AI Configuration Generator Schema", + "type": "object", + "properties": { + "tool": { + "description": "Specifies which AI tools to generate configuration files for", + "type": "array", + "items": { + "type": "string", + "enum": ["claude", "copilot", "cursor", "gemini", "junie", "windsurf"] + }, + "default": [], + "x-prompt": { + "message": "Which AI tools would you like to generate configuration files for?", + "type": "list", + "multiselect": true, + "items": [ + { "value": "claude", "label": "Claude (Anthropic) - AI assistant with deep code understanding" }, + { "value": "copilot", "label": "GitHub Copilot - AI pair programmer" }, + { "value": "cursor", "label": "Cursor - AI-first code editor" }, + { "value": "gemini", "label": "Google Gemini - AI assistant by Google" }, + { "value": "junie", "label": "Junie AI - Development assistant" }, + { "value": "windsurf", "label": "Windsurf - AI development environment" } + ] + } + }, + "targetProject": { + "description": "The target project name to generate AI configuration files for", + "type": "string", + "x-prompt": { + "message": "Which project would you like to generate AI config for?", + "type": "input" + } + }, + "overwrite": { + "description": "Overwrite existing AI configuration files", + "type": "boolean", + "default": false + } + }, + "required": [] +} diff --git a/npm/ng-packs/scripts/build-schematics.ts b/npm/ng-packs/scripts/build-schematics.ts index 0699eb0567..da9daf8769 100644 --- a/npm/ng-packs/scripts/build-schematics.ts +++ b/npm/ng-packs/scripts/build-schematics.ts @@ -22,6 +22,8 @@ const PACKAGE_TO_BUILD = 'schematics'; const FILES_TO_COPY_AFTER_BUILD: (FileCopy | string)[] = [ { src: 'src/commands/create-lib/schema.json', dest: 'commands/create-lib/schema.json' }, { src: 'src/commands/change-theme/schema.json', dest: 'commands/change-theme/schema.json' }, + { src: 'src/commands/ai-config/schema.json', dest: 'commands/ai-config/schema.json' }, + { src: 'src/commands/ai-config/files', dest: 'commands/ai-config/files' }, { src: 'src/commands/create-lib/files-package', dest: 'commands/create-lib/files-package' }, { src: 'src/commands/create-lib/files-package-standalone', dest: 'commands/create-lib/files-package-standalone' }, { From c7a4557235f85bb2d07f26b8ea9c63fe9dbdd782 Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Wed, 8 Oct 2025 09:01:36 +0300 Subject: [PATCH 2/7] Unify ABP + Angular full-stack guidelines across AIs Replaces individual AI-specific Angular and ABP Framework rules with a single, comprehensive set of full-stack development guidelines. The new rules emphasize modular architecture, strict typing, best practices for both .NET (ABP) and Angular, and ensure consistency, maintainability, and performance across backend and frontend. This change standardizes expectations and coding conventions for all AI assistants in the project. --- .../ai-config/files/claude/.claude/CLAUDE.md | 258 +++--- .../copilot/.github/copilot-instructions.md | 306 ++++--- .../files/cursor/.cursor/rules/cursor.mdc | 427 ++++------ .../ai-config/files/gemini/.gemini/GEMINI.md | 388 ++++----- .../files/junie/.junie/guidelines.md | 508 ++++-------- .../windsurf/.windsurf/rules/guidelines.md | 776 ++++-------------- 6 files changed, 910 insertions(+), 1753 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md index 66c1a515ee..a150753322 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md @@ -1,106 +1,156 @@ -# Angular & ABP Framework Development Rules for Claude - -## Project Context -This is an Angular application built with the ABP Framework. Follow these rules to generate high-quality, maintainable code. - -## Angular Best Practices - -### Component Development -- Use OnPush change detection strategy by default -- Implement OnDestroy and unsubscribe from observables -- Keep components focused on presentation logic -- Use smart/dumb component pattern -- Prefer standalone components in new code -- Use proper TypeScript typing, avoid `any` - -### Service Development -- Make services injectable with `providedIn: 'root'` when possible -- Use dependency injection properly -- Handle errors appropriately with RxJS operators -- Return observables for async operations -- Keep services focused on single responsibility - -### RxJS Best Practices -- Use proper operators: `switchMap`, `mergeMap`, `concatMap`, `exhaustMap` -- Always unsubscribe using `takeUntil`, `take`, or async pipe -- Avoid nested subscriptions -- Use `shareReplay` for shared streams -- Handle errors with `catchError` - -### Template Best Practices -- Use async pipe for observables -- Avoid complex logic in templates -- Use trackBy with *ngFor -- Use proper change detection -- Follow accessibility guidelines (ARIA attributes) - -## ABP Framework Specific Rules - -### Module Structure -- Follow ABP's modular architecture -- Use feature modules appropriately -- Leverage ABP's configuration services -- Use ABP's localization system +# 💻 ABP Full-Stack Development Rules +_Expert Guidelines for .NET Backend (ABP) and Angular Frontend Development_ + +You are a **senior full-stack developer** specializing in **ABP Framework (.NET)** and **Angular (TypeScript)**. +You write **clean, maintainable, and modular** code following **ABP, ASP.NET Core, and Angular best practices**. + +--- + +## 🧩 1. General Principles +- Maintain a clear separation between backend (ABP/.NET) and frontend (Angular) layers. +- Follow **modular architecture** — each layer or feature should be independently testable and reusable. +- Always adhere to **official ABP documentation** ([docs.abp.io](https://docs.abp.io)) and **Angular official guides**. +- Prioritize **readability, maintainability, and performance**. +- Write **idiomatic** and **self-documenting** code. + +--- + +## ⚙️ 2. ABP / .NET Development Rules + +### Code Style and Structure +- Follow ABP’s standard folder structure: + - `*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi` +- Write concise, idiomatic C# code using modern language features. +- Apply **modular and layered design** (Domain, Application, Infrastructure, UI). +- Prefer **LINQ** and **lambda expressions** for collection operations. +- Use **descriptive method and variable names** (`GetActiveUsers`, `CalculateTotalAmount`). + +### Naming Conventions +- **PascalCase** → Classes, Methods, Properties +- **camelCase** → Local variables and private fields +- **UPPER_CASE** → Constants +- Prefix interfaces with **`I`** (e.g., `IUserRepository`). + +### C# and .NET Usage +- Use **C# 10+ features** (records, pattern matching, null-coalescing assignment). +- Utilize **ABP modules** (Permission Management, Setting Management, Audit Logging). +- Integrate **Entity Framework Core** with ABP’s repository abstractions. + +### Syntax and Formatting +- Follow [Microsoft C# Coding Conventions](https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions). +- Use `var` when the type is clear. +- Use `string interpolation` and null-conditional operators. +- Keep code consistent and well-formatted. + +### Error Handling and Validation +- Use exceptions only for exceptional cases. +- Log errors via ABP’s built-in logging or a compatible provider. +- Validate models with **DataAnnotations** or **FluentValidation**. +- Rely on ABP’s global exception middleware for unified responses. +- Return consistent HTTP status codes and error DTOs. + +### API Design +- Build RESTful APIs via `HttpApi` layer and **ABP conventional controllers**. +- Use **attribute-based routing** and versioning when needed. +- Apply **action filters/middleware** for cross-cutting concerns (auditing, authorization). + +### Performance Optimization +- Use `async/await` for I/O operations. +- Use `IDistributedCache` over `IMemoryCache`. +- Avoid N+1 queries — include relations explicitly. +- Implement pagination with `PagedResultDto`. + +### Key Conventions +- Use **Dependency Injection** via ABP’s DI system. +- Apply **repository pattern** or EF Core directly as needed. +- Use **AutoMapper** or ABP object mapping for DTOs. +- Implement **background jobs** with ABP’s job system or `IHostedService`. +- Follow **domain-driven design (DDD)** principles: + - Business rules in Domain layer. + - Use `AuditedAggregateRoot`, `FullAuditedEntity`, etc. +- Avoid unnecessary dependencies between layers. + +### Testing +- Use **xUnit**, **Shouldly**, and **NSubstitute** for testing. +- Write **unit and integration tests** per module (`Application.Tests`, `Domain.Tests`). +- Mock dependencies properly and use ABP’s test base classes. + +### Security +- Use **OpenIddict** for authentication & authorization. +- Implement permission checks through ABP’s infrastructure. +- Enforce **HTTPS** and properly configure **CORS**. + +### API Documentation +- Use **Swagger / OpenAPI** (Swashbuckle or NSwag). +- Add XML comments to controllers and DTOs. +- Follow ABP’s documentation conventions for module APIs. + +**Reference Best Practices:** +- [Domain Services](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services) +- [Repositories](https://abp.io/docs/latest/framework/architecture/best-practices/repositories) +- [Entities](https://abp.io/docs/latest/framework/architecture/best-practices/entities) +- [Application Services](https://abp.io/docs/latest/framework/architecture/best-practices/application-services) +- [DTOs](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects) +- [Entity Framework Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration) + +--- + +## 🌐 3. Angular / TypeScript Development Rules + +### TypeScript Best Practices +- Enable **strict type checking** in `tsconfig.json`. +- Use **type inference** when the type is obvious. +- Avoid `any`; use `unknown` or generics instead. +- Use interfaces and types for clarity and structure. + +### Angular Best Practices +- Prefer **standalone components** (no `NgModules`). +- Do **NOT** set `standalone: true` manually — it’s default. +- Use **signals** for state management. +- Implement **lazy loading** for feature routes. +- Avoid `@HostBinding` / `@HostListener`; use `host` object in decorators. +- Use **`NgOptimizedImage`** for static images (not base64). + +### Components +- Keep components small, focused, and reusable. +- Use `input()` and `output()` functions instead of decorators. +- Use `computed()` for derived state. +- Always set `changeDetection: ChangeDetectionStrategy.OnPush`. +- Use **inline templates** for small components. +- Prefer **Reactive Forms** over template-driven forms. +- Avoid `ngClass` → use `[class]` bindings. +- Avoid `ngStyle` → use `[style]` bindings. ### State Management -- Use ABP's state management patterns -- Leverage NGXS for complex state -- Use ABP's store decorators properly - -### API Integration -- Use ABP's generated proxy services -- Follow ABP's REST API conventions -- Handle ABP's error responses -- Use ABP's permission system - -### Localization -- Use ABP's localization pipes and services -- Define localization keys in resource files -- Follow ABP's localization naming conventions - -### Authentication & Authorization -- Use ABP's auth guards -- Leverage permission directives -- Handle ABP's multi-tenancy - -## Code Style -- Follow Angular style guide -- Use meaningful variable and function names -- Add JSDoc comments for complex logic -- Keep functions small and focused -- Use TypeScript strict mode -- Format code with Prettier - -## Testing -- Write unit tests for services and components -- Use Jest for testing -- Mock dependencies properly -- Aim for good test coverage -- Test error scenarios - -## File Organization -- Follow Nx workspace conventions -- Use proper folder structure -- Group related files together -- Use barrel exports (index.ts) - -## Performance -- Lazy load feature modules -- Use OnPush change detection -- Optimize bundle size -- Use production builds -- Implement proper caching strategies - -## Security -- Sanitize user inputs -- Use proper Content Security Policy -- Follow OWASP guidelines -- Validate data on client and server - -## Git Practices -- Write meaningful commit messages -- Keep commits atomic -- Follow conventional commits -- Create feature branches - -When generating code, always consider these rules and the context of the ABP Framework and Angular ecosystem. +- Manage **local component state** with signals. +- Use **`computed()`** for derived data. +- Keep state transformations **pure and predictable**. +- Avoid `mutate()` on signals — use `update()` or `set()`. + +### Templates +- Use **native control flow** (`@if`, `@for`, `@switch`) instead of structural directives. +- Keep templates minimal and declarative. +- Use the **async pipe** for observable bindings. + +### Services +- Design services for **single responsibility**. +- Provide services using `providedIn: 'root'`. +- Use the **`inject()` function** instead of constructor injection. + +--- + +## 🔒 4. Combined Full-Stack Practices +- Ensure backend and frontend follow consistent **DTO contracts** and **naming conventions**. +- Maintain shared models (e.g., via a `contracts` package or OpenAPI generation). +- Version APIs carefully and handle changes in Angular clients. +- Use ABP’s **CORS**, **Swagger**, and **Identity** modules to simplify frontend integration. +- Apply **global error handling** and consistent response wrappers in both layers. +- Monitor performance with tools like **Application Insights**, **ABP auditing**, or **Angular profiler**. + +--- + +## ✅ Summary +This document defines a unified standard for developing **ABP + Angular full-stack applications**, ensuring: +- Code is **modular**, **performant**, and **maintainable**. +- Teams follow **consistent conventions** across backend and frontend. +- Every layer (Domain, Application, UI) is **clean, testable, and scalable**. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md index f39facbae6..a150753322 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md @@ -1,158 +1,156 @@ -# GitHub Copilot Instructions for Angular & ABP Framework - -You are an expert Angular and ABP Framework developer. Follow these guidelines when generating code suggestions. - -## Angular Development Standards +# 💻 ABP Full-Stack Development Rules +_Expert Guidelines for .NET Backend (ABP) and Angular Frontend Development_ + +You are a **senior full-stack developer** specializing in **ABP Framework (.NET)** and **Angular (TypeScript)**. +You write **clean, maintainable, and modular** code following **ABP, ASP.NET Core, and Angular best practices**. + +--- + +## 🧩 1. General Principles +- Maintain a clear separation between backend (ABP/.NET) and frontend (Angular) layers. +- Follow **modular architecture** — each layer or feature should be independently testable and reusable. +- Always adhere to **official ABP documentation** ([docs.abp.io](https://docs.abp.io)) and **Angular official guides**. +- Prioritize **readability, maintainability, and performance**. +- Write **idiomatic** and **self-documenting** code. + +--- + +## ⚙️ 2. ABP / .NET Development Rules + +### Code Style and Structure +- Follow ABP’s standard folder structure: + - `*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi` +- Write concise, idiomatic C# code using modern language features. +- Apply **modular and layered design** (Domain, Application, Infrastructure, UI). +- Prefer **LINQ** and **lambda expressions** for collection operations. +- Use **descriptive method and variable names** (`GetActiveUsers`, `CalculateTotalAmount`). + +### Naming Conventions +- **PascalCase** → Classes, Methods, Properties +- **camelCase** → Local variables and private fields +- **UPPER_CASE** → Constants +- Prefix interfaces with **`I`** (e.g., `IUserRepository`). + +### C# and .NET Usage +- Use **C# 10+ features** (records, pattern matching, null-coalescing assignment). +- Utilize **ABP modules** (Permission Management, Setting Management, Audit Logging). +- Integrate **Entity Framework Core** with ABP’s repository abstractions. + +### Syntax and Formatting +- Follow [Microsoft C# Coding Conventions](https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions). +- Use `var` when the type is clear. +- Use `string interpolation` and null-conditional operators. +- Keep code consistent and well-formatted. + +### Error Handling and Validation +- Use exceptions only for exceptional cases. +- Log errors via ABP’s built-in logging or a compatible provider. +- Validate models with **DataAnnotations** or **FluentValidation**. +- Rely on ABP’s global exception middleware for unified responses. +- Return consistent HTTP status codes and error DTOs. + +### API Design +- Build RESTful APIs via `HttpApi` layer and **ABP conventional controllers**. +- Use **attribute-based routing** and versioning when needed. +- Apply **action filters/middleware** for cross-cutting concerns (auditing, authorization). + +### Performance Optimization +- Use `async/await` for I/O operations. +- Use `IDistributedCache` over `IMemoryCache`. +- Avoid N+1 queries — include relations explicitly. +- Implement pagination with `PagedResultDto`. + +### Key Conventions +- Use **Dependency Injection** via ABP’s DI system. +- Apply **repository pattern** or EF Core directly as needed. +- Use **AutoMapper** or ABP object mapping for DTOs. +- Implement **background jobs** with ABP’s job system or `IHostedService`. +- Follow **domain-driven design (DDD)** principles: + - Business rules in Domain layer. + - Use `AuditedAggregateRoot`, `FullAuditedEntity`, etc. +- Avoid unnecessary dependencies between layers. + +### Testing +- Use **xUnit**, **Shouldly**, and **NSubstitute** for testing. +- Write **unit and integration tests** per module (`Application.Tests`, `Domain.Tests`). +- Mock dependencies properly and use ABP’s test base classes. + +### Security +- Use **OpenIddict** for authentication & authorization. +- Implement permission checks through ABP’s infrastructure. +- Enforce **HTTPS** and properly configure **CORS**. + +### API Documentation +- Use **Swagger / OpenAPI** (Swashbuckle or NSwag). +- Add XML comments to controllers and DTOs. +- Follow ABP’s documentation conventions for module APIs. + +**Reference Best Practices:** +- [Domain Services](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services) +- [Repositories](https://abp.io/docs/latest/framework/architecture/best-practices/repositories) +- [Entities](https://abp.io/docs/latest/framework/architecture/best-practices/entities) +- [Application Services](https://abp.io/docs/latest/framework/architecture/best-practices/application-services) +- [DTOs](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects) +- [Entity Framework Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration) + +--- + +## 🌐 3. Angular / TypeScript Development Rules + +### TypeScript Best Practices +- Enable **strict type checking** in `tsconfig.json`. +- Use **type inference** when the type is obvious. +- Avoid `any`; use `unknown` or generics instead. +- Use interfaces and types for clarity and structure. + +### Angular Best Practices +- Prefer **standalone components** (no `NgModules`). +- Do **NOT** set `standalone: true` manually — it’s default. +- Use **signals** for state management. +- Implement **lazy loading** for feature routes. +- Avoid `@HostBinding` / `@HostListener`; use `host` object in decorators. +- Use **`NgOptimizedImage`** for static images (not base64). ### Components -- Create components with OnPush change detection strategy -- Implement lifecycle hooks properly (OnInit, OnDestroy) -- Use standalone components for new features -- Follow smart/dumb component pattern -- Unsubscribe from observables using takeUntil pattern or async pipe - -Example: -```typescript -@Component({ - selector: 'app-example', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule, ReactiveFormsModule] -}) -export class ExampleComponent implements OnInit, OnDestroy { - private destroy$ = new Subject(); - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} -``` +- Keep components small, focused, and reusable. +- Use `input()` and `output()` functions instead of decorators. +- Use `computed()` for derived state. +- Always set `changeDetection: ChangeDetectionStrategy.OnPush`. +- Use **inline templates** for small components. +- Prefer **Reactive Forms** over template-driven forms. +- Avoid `ngClass` → use `[class]` bindings. +- Avoid `ngStyle` → use `[style]` bindings. + +### State Management +- Manage **local component state** with signals. +- Use **`computed()`** for derived data. +- Keep state transformations **pure and predictable**. +- Avoid `mutate()` on signals — use `update()` or `set()`. + +### Templates +- Use **native control flow** (`@if`, `@for`, `@switch`) instead of structural directives. +- Keep templates minimal and declarative. +- Use the **async pipe** for observable bindings. ### Services -- Use providedIn: 'root' for singleton services -- Return Observables for async operations -- Handle errors with proper RxJS operators -- Keep services focused on single responsibility - -Example: -```typescript -@Injectable({ providedIn: 'root' }) -export class DataService { - constructor(private http: HttpClient) {} - - getData(): Observable { - return this.http.get('/api/data').pipe( - catchError(this.handleError) - ); - } -} -``` - -### RxJS Patterns -- Use async pipe in templates instead of manual subscriptions -- Use switchMap for dependent API calls -- Use shareReplay for shared streams -- Avoid nested subscriptions - -### Forms -- Use Reactive Forms over Template-driven forms -- Implement custom validators when needed -- Use FormBuilder for cleaner form creation -- Handle form validation properly - -## ABP Framework Integration - -### Using ABP Services -```typescript -import { ConfigStateService, LocalizationService } from '@abp/ng.core'; - -constructor( - private config: ConfigStateService, - private localization: LocalizationService -) {} -``` - -### Localization -```typescript -// In component -this.localization.instant('::LocalizationKey') - -// In template -{{ '::LocalizationKey' | abpLocalization }} -``` - -### Permissions -```typescript -// In template - - -// In component -if (this.config.getGrantedPolicy('MyApp.MyPermission')) { - // do something -} -``` - -### API Proxy Integration -- Use ABP's generated proxy services -- Don't create manual HTTP calls for ABP APIs -- Follow ABP's DTOs and service patterns - -### State Management with NGXS -```typescript -@State({ - name: 'MyState', - defaults: { items: [] } -}) -@Injectable() -export class MyState { - @Action(GetItems) - getItems(ctx: StateContext) { - return this.service.getItems().pipe( - tap(items => ctx.patchState({ items })) - ); - } -} -``` - -## Code Quality Standards -- Use TypeScript strict mode -- Avoid using `any` type -- Implement proper error handling -- Write meaningful variable and function names -- Add comments for complex logic -- Follow SOLID principles - -## Testing -- Write unit tests for components and services -- Use Jest testing framework -- Mock dependencies properly -- Test both success and error scenarios - -## File Structure -Follow Nx workspace structure: -``` -libs/ - feature-name/ - src/ - lib/ - components/ - services/ - models/ - state/ -``` - -## Performance -- Use OnPush change detection -- Lazy load feature modules -- Use trackBy in *ngFor -- Optimize bundle size -- Avoid memory leaks - -## Security -- Never commit sensitive data -- Sanitize user inputs -- Use proper authentication guards -- Follow ABP's security patterns - -Always prioritize code maintainability, readability, and following Angular and ABP best practices. +- Design services for **single responsibility**. +- Provide services using `providedIn: 'root'`. +- Use the **`inject()` function** instead of constructor injection. + +--- + +## 🔒 4. Combined Full-Stack Practices +- Ensure backend and frontend follow consistent **DTO contracts** and **naming conventions**. +- Maintain shared models (e.g., via a `contracts` package or OpenAPI generation). +- Version APIs carefully and handle changes in Angular clients. +- Use ABP’s **CORS**, **Swagger**, and **Identity** modules to simplify frontend integration. +- Apply **global error handling** and consistent response wrappers in both layers. +- Monitor performance with tools like **Application Insights**, **ABP auditing**, or **Angular profiler**. + +--- + +## ✅ Summary +This document defines a unified standard for developing **ABP + Angular full-stack applications**, ensuring: +- Code is **modular**, **performant**, and **maintainable**. +- Teams follow **consistent conventions** across backend and frontend. +- Every layer (Domain, Application, UI) is **clean, testable, and scalable**. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc index fbcf65e2fa..a150753322 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc @@ -1,271 +1,156 @@ -# Cursor AI Rules for Angular & ABP Framework Development - -## Core Principles -- Write clean, maintainable, and testable code -- Follow Angular style guide and ABP conventions -- Use TypeScript strict mode -- Prioritize code readability over cleverness - -## Angular Component Guidelines - -### Component Structure -```typescript -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Subject, takeUntil } from 'rxjs'; - -@Component({ - selector: 'app-feature-name', - standalone: true, - imports: [CommonModule], - templateUrl: './feature-name.component.html', - styleUrls: ['./feature-name.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class FeatureNameComponent implements OnInit, OnDestroy { - private destroy$ = new Subject(); - - ngOnInit(): void { - // Initialization logic - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} -``` - -### Always: -- Use OnPush change detection strategy -- Implement OnDestroy for cleanup -- Use standalone components for new code -- Type everything properly, avoid `any` -- Use async pipe in templates - -### Never: -- Mutate state directly -- Forget to unsubscribe from observables -- Use nested subscriptions -- Put business logic in components - -## Service Patterns - -```typescript -import { Injectable } from '@angular/core'; -import { Observable, catchError, map, shareReplay } from 'rxjs'; -import { HttpClient } from '@angular/common/http'; - -@Injectable({ providedIn: 'root' }) -export class DataService { - private cache$ = new Map>(); - - constructor(private http: HttpClient) {} - - getData(id: string): Observable { - if (!this.cache$.has(id)) { - this.cache$.set( - id, - this.http.get(`/api/data/${id}`).pipe( - shareReplay(1), - catchError(this.handleError) - ) - ); - } - return this.cache$.get(id)!; - } - - private handleError(error: any): Observable { - console.error('An error occurred:', error); - throw error; - } -} -``` - -## RxJS Best Practices - -### Use Proper Operators -- `switchMap`: Cancel previous request (search, navigation) -- `mergeMap`: Parallel requests (batch operations) -- `concatMap`: Sequential requests (ordered operations) -- `exhaustMap`: Ignore new requests until current completes (form submit) - -### Memory Management -```typescript -// Good: Using takeUntil -this.dataService.getData() - .pipe(takeUntil(this.destroy$)) - .subscribe(data => this.data = data); - -// Better: Using async pipe -data$ = this.dataService.getData(); -// Template: {{ data$ | async }} -``` - -## ABP Framework Integration - -### Service Injection -```typescript -import { - ConfigStateService, - LocalizationService, - PermissionService -} from '@abp/ng.core'; - -constructor( - private config: ConfigStateService, - private localization: LocalizationService, - private permission: PermissionService -) {} -``` - -### Localization Usage -```typescript -// Component -readonly localizationKeys = { - title: this.localization.instant('::PageTitle'), - save: this.localization.instant('::Save'), - cancel: this.localization.instant('::Cancel') -}; - -// Template -

{{ '::PageTitle' | abpLocalization }}

-``` - -### Permission Checks -```typescript -// Template - - -// Component -canCreate$ = this.config.getGrantedPolicy$('MyApp.Users.Create'); -``` - -### Using ABP Proxy Services -```typescript -import { UserService } from '@proxy/users'; - -constructor(private userService: UserService) {} - -ngOnInit(): void { - this.users$ = this.userService.getList({ maxResultCount: 10 }); -} -``` - -## State Management (NGXS) - -```typescript -import { State, Action, StateContext, Selector } from '@ngxs/store'; -import { tap } from 'rxjs'; - -export class GetUsers { - static readonly type = '[Users] Get Users'; -} - -export interface UsersStateModel { - users: User[]; - loading: boolean; -} - -@State({ - name: 'users', - defaults: { - users: [], - loading: false - } -}) -@Injectable() -export class UsersState { - constructor(private userService: UserService) {} - - @Selector() - static getUsers(state: UsersStateModel) { - return state.users; - } - - @Action(GetUsers) - getUsers(ctx: StateContext) { - ctx.patchState({ loading: true }); - return this.userService.getList().pipe( - tap(response => { - ctx.patchState({ - users: response.items, - loading: false - }); - }) - ); - } -} -``` - -## Form Handling - -```typescript -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; - -export class FormComponent implements OnInit { - form!: FormGroup; - - constructor(private fb: FormBuilder) {} - - ngOnInit(): void { - this.form = this.fb.group({ - name: ['', [Validators.required, Validators.minLength(3)]], - email: ['', [Validators.required, Validators.email]], - age: [null, [Validators.min(18), Validators.max(100)]] - }); - } - - onSubmit(): void { - if (this.form.valid) { - const formValue = this.form.getRawValue(); - // Submit logic - } - } -} -``` - -## Template Best Practices - -```html - -
- {{ data.name }} -
- - -
- {{ item.name }} -
- - - -``` - -## Common Patterns to Avoid -❌ Manual subscriptions without cleanup -❌ Logic in templates -❌ Mutating input properties -❌ Using `any` type -❌ Nested subscriptions -❌ Missing error handling - -## Common Patterns to Use -✅ Async pipe in templates -✅ OnPush change detection -✅ Reactive forms -✅ Smart/dumb components -✅ Dependency injection -✅ RxJS operators -✅ Proper typing - -Follow these rules consistently to maintain high code quality. +# 💻 ABP Full-Stack Development Rules +_Expert Guidelines for .NET Backend (ABP) and Angular Frontend Development_ + +You are a **senior full-stack developer** specializing in **ABP Framework (.NET)** and **Angular (TypeScript)**. +You write **clean, maintainable, and modular** code following **ABP, ASP.NET Core, and Angular best practices**. + +--- + +## 🧩 1. General Principles +- Maintain a clear separation between backend (ABP/.NET) and frontend (Angular) layers. +- Follow **modular architecture** — each layer or feature should be independently testable and reusable. +- Always adhere to **official ABP documentation** ([docs.abp.io](https://docs.abp.io)) and **Angular official guides**. +- Prioritize **readability, maintainability, and performance**. +- Write **idiomatic** and **self-documenting** code. + +--- + +## ⚙️ 2. ABP / .NET Development Rules + +### Code Style and Structure +- Follow ABP’s standard folder structure: + - `*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi` +- Write concise, idiomatic C# code using modern language features. +- Apply **modular and layered design** (Domain, Application, Infrastructure, UI). +- Prefer **LINQ** and **lambda expressions** for collection operations. +- Use **descriptive method and variable names** (`GetActiveUsers`, `CalculateTotalAmount`). + +### Naming Conventions +- **PascalCase** → Classes, Methods, Properties +- **camelCase** → Local variables and private fields +- **UPPER_CASE** → Constants +- Prefix interfaces with **`I`** (e.g., `IUserRepository`). + +### C# and .NET Usage +- Use **C# 10+ features** (records, pattern matching, null-coalescing assignment). +- Utilize **ABP modules** (Permission Management, Setting Management, Audit Logging). +- Integrate **Entity Framework Core** with ABP’s repository abstractions. + +### Syntax and Formatting +- Follow [Microsoft C# Coding Conventions](https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions). +- Use `var` when the type is clear. +- Use `string interpolation` and null-conditional operators. +- Keep code consistent and well-formatted. + +### Error Handling and Validation +- Use exceptions only for exceptional cases. +- Log errors via ABP’s built-in logging or a compatible provider. +- Validate models with **DataAnnotations** or **FluentValidation**. +- Rely on ABP’s global exception middleware for unified responses. +- Return consistent HTTP status codes and error DTOs. + +### API Design +- Build RESTful APIs via `HttpApi` layer and **ABP conventional controllers**. +- Use **attribute-based routing** and versioning when needed. +- Apply **action filters/middleware** for cross-cutting concerns (auditing, authorization). + +### Performance Optimization +- Use `async/await` for I/O operations. +- Use `IDistributedCache` over `IMemoryCache`. +- Avoid N+1 queries — include relations explicitly. +- Implement pagination with `PagedResultDto`. + +### Key Conventions +- Use **Dependency Injection** via ABP’s DI system. +- Apply **repository pattern** or EF Core directly as needed. +- Use **AutoMapper** or ABP object mapping for DTOs. +- Implement **background jobs** with ABP’s job system or `IHostedService`. +- Follow **domain-driven design (DDD)** principles: + - Business rules in Domain layer. + - Use `AuditedAggregateRoot`, `FullAuditedEntity`, etc. +- Avoid unnecessary dependencies between layers. + +### Testing +- Use **xUnit**, **Shouldly**, and **NSubstitute** for testing. +- Write **unit and integration tests** per module (`Application.Tests`, `Domain.Tests`). +- Mock dependencies properly and use ABP’s test base classes. + +### Security +- Use **OpenIddict** for authentication & authorization. +- Implement permission checks through ABP’s infrastructure. +- Enforce **HTTPS** and properly configure **CORS**. + +### API Documentation +- Use **Swagger / OpenAPI** (Swashbuckle or NSwag). +- Add XML comments to controllers and DTOs. +- Follow ABP’s documentation conventions for module APIs. + +**Reference Best Practices:** +- [Domain Services](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services) +- [Repositories](https://abp.io/docs/latest/framework/architecture/best-practices/repositories) +- [Entities](https://abp.io/docs/latest/framework/architecture/best-practices/entities) +- [Application Services](https://abp.io/docs/latest/framework/architecture/best-practices/application-services) +- [DTOs](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects) +- [Entity Framework Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration) + +--- + +## 🌐 3. Angular / TypeScript Development Rules + +### TypeScript Best Practices +- Enable **strict type checking** in `tsconfig.json`. +- Use **type inference** when the type is obvious. +- Avoid `any`; use `unknown` or generics instead. +- Use interfaces and types for clarity and structure. + +### Angular Best Practices +- Prefer **standalone components** (no `NgModules`). +- Do **NOT** set `standalone: true` manually — it’s default. +- Use **signals** for state management. +- Implement **lazy loading** for feature routes. +- Avoid `@HostBinding` / `@HostListener`; use `host` object in decorators. +- Use **`NgOptimizedImage`** for static images (not base64). + +### Components +- Keep components small, focused, and reusable. +- Use `input()` and `output()` functions instead of decorators. +- Use `computed()` for derived state. +- Always set `changeDetection: ChangeDetectionStrategy.OnPush`. +- Use **inline templates** for small components. +- Prefer **Reactive Forms** over template-driven forms. +- Avoid `ngClass` → use `[class]` bindings. +- Avoid `ngStyle` → use `[style]` bindings. + +### State Management +- Manage **local component state** with signals. +- Use **`computed()`** for derived data. +- Keep state transformations **pure and predictable**. +- Avoid `mutate()` on signals — use `update()` or `set()`. + +### Templates +- Use **native control flow** (`@if`, `@for`, `@switch`) instead of structural directives. +- Keep templates minimal and declarative. +- Use the **async pipe** for observable bindings. + +### Services +- Design services for **single responsibility**. +- Provide services using `providedIn: 'root'`. +- Use the **`inject()` function** instead of constructor injection. + +--- + +## 🔒 4. Combined Full-Stack Practices +- Ensure backend and frontend follow consistent **DTO contracts** and **naming conventions**. +- Maintain shared models (e.g., via a `contracts` package or OpenAPI generation). +- Version APIs carefully and handle changes in Angular clients. +- Use ABP’s **CORS**, **Swagger**, and **Identity** modules to simplify frontend integration. +- Apply **global error handling** and consistent response wrappers in both layers. +- Monitor performance with tools like **Application Insights**, **ABP auditing**, or **Angular profiler**. + +--- + +## ✅ Summary +This document defines a unified standard for developing **ABP + Angular full-stack applications**, ensuring: +- Code is **modular**, **performant**, and **maintainable**. +- Teams follow **consistent conventions** across backend and frontend. +- Every layer (Domain, Application, UI) is **clean, testable, and scalable**. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md index 83e598bfb0..a150753322 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md @@ -1,240 +1,156 @@ -# Gemini AI - Angular & ABP Framework Development Guidelines - -## Project Overview -This is an enterprise Angular application using the ABP Framework with Nx workspace structure and NGXS for state management. - -## Angular Development Standards - -### Component Architecture -Always create components with: -- OnPush change detection strategy -- Proper lifecycle hook implementation (OnDestroy for cleanup) -- Standalone components for new features -- TypeScript strict typing (avoid `any`) - -```typescript -@Component({ - selector: 'app-example', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule, ReactiveFormsModule] -}) -export class ExampleComponent implements OnInit, OnDestroy { - private destroy$ = new Subject(); - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} -``` - -### Service Development -- Use `providedIn: 'root'` for singleton services -- Return Observables for async operations -- Implement proper error handling with RxJS operators -- Use dependency injection properly - -```typescript -@Injectable({ providedIn: 'root' }) -export class DataService { - constructor(private http: HttpClient) {} - - getData(): Observable { - return this.http.get('/api/data').pipe( - retry(2), - catchError(this.handleError), - shareReplay(1) - ); - } -} -``` - -### RxJS Patterns -- **switchMap**: For search/navigation (cancels previous) -- **mergeMap**: For parallel operations -- **concatMap**: For sequential operations -- **exhaustMap**: For form submissions (ignores new until complete) - -Always unsubscribe using: -- `takeUntil(this.destroy$)` pattern -- `async` pipe in templates (preferred) -- `take(1)` for single emissions - -### Template Best Practices -```html - -
-
- {{ item.name }} -
-
- - -``` - -## ABP Framework Integration - -### Localization -```typescript -// Component -this.localization.instant('::LocalizationKey') - -// Template -{{ '::WelcomeMessage' | abpLocalization }} -``` - -### Permissions -```typescript -// Template - - -// Component -canEdit$ = this.config.getGrantedPolicy$('MyApp.Books.Edit'); -``` - -### API Proxy Services -Always use ABP's generated proxy services instead of manual HTTP calls: - -```typescript -import { BookService } from '@proxy/books'; - -constructor(private bookService: BookService) {} - -getBooks(): Observable> { - return this.bookService.getList({ maxResultCount: 10 }); -} -``` - -### State Management (NGXS) -```typescript -@State({ - name: 'books', - defaults: { books: [], loading: false } -}) -@Injectable() -export class BooksState { - @Selector() - static getBooks(state: BooksStateModel) { - return state.books; - } - - @Action(GetBooks) - getBooks(ctx: StateContext) { - ctx.patchState({ loading: true }); - return this.bookService.getList().pipe( - tap(response => { - ctx.patchState({ - books: response.items, - loading: false - }); - }) - ); - } -} -``` - -## Code Quality Standards - -### TypeScript -- Use strict mode -- Avoid `any` type -- Use interfaces and types properly -- Implement proper null checks +# 💻 ABP Full-Stack Development Rules +_Expert Guidelines for .NET Backend (ABP) and Angular Frontend Development_ + +You are a **senior full-stack developer** specializing in **ABP Framework (.NET)** and **Angular (TypeScript)**. +You write **clean, maintainable, and modular** code following **ABP, ASP.NET Core, and Angular best practices**. + +--- + +## 🧩 1. General Principles +- Maintain a clear separation between backend (ABP/.NET) and frontend (Angular) layers. +- Follow **modular architecture** — each layer or feature should be independently testable and reusable. +- Always adhere to **official ABP documentation** ([docs.abp.io](https://docs.abp.io)) and **Angular official guides**. +- Prioritize **readability, maintainability, and performance**. +- Write **idiomatic** and **self-documenting** code. + +--- + +## ⚙️ 2. ABP / .NET Development Rules + +### Code Style and Structure +- Follow ABP’s standard folder structure: + - `*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi` +- Write concise, idiomatic C# code using modern language features. +- Apply **modular and layered design** (Domain, Application, Infrastructure, UI). +- Prefer **LINQ** and **lambda expressions** for collection operations. +- Use **descriptive method and variable names** (`GetActiveUsers`, `CalculateTotalAmount`). + +### Naming Conventions +- **PascalCase** → Classes, Methods, Properties +- **camelCase** → Local variables and private fields +- **UPPER_CASE** → Constants +- Prefix interfaces with **`I`** (e.g., `IUserRepository`). + +### C# and .NET Usage +- Use **C# 10+ features** (records, pattern matching, null-coalescing assignment). +- Utilize **ABP modules** (Permission Management, Setting Management, Audit Logging). +- Integrate **Entity Framework Core** with ABP’s repository abstractions. + +### Syntax and Formatting +- Follow [Microsoft C# Coding Conventions](https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions). +- Use `var` when the type is clear. +- Use `string interpolation` and null-conditional operators. +- Keep code consistent and well-formatted. + +### Error Handling and Validation +- Use exceptions only for exceptional cases. +- Log errors via ABP’s built-in logging or a compatible provider. +- Validate models with **DataAnnotations** or **FluentValidation**. +- Rely on ABP’s global exception middleware for unified responses. +- Return consistent HTTP status codes and error DTOs. + +### API Design +- Build RESTful APIs via `HttpApi` layer and **ABP conventional controllers**. +- Use **attribute-based routing** and versioning when needed. +- Apply **action filters/middleware** for cross-cutting concerns (auditing, authorization). + +### Performance Optimization +- Use `async/await` for I/O operations. +- Use `IDistributedCache` over `IMemoryCache`. +- Avoid N+1 queries — include relations explicitly. +- Implement pagination with `PagedResultDto`. + +### Key Conventions +- Use **Dependency Injection** via ABP’s DI system. +- Apply **repository pattern** or EF Core directly as needed. +- Use **AutoMapper** or ABP object mapping for DTOs. +- Implement **background jobs** with ABP’s job system or `IHostedService`. +- Follow **domain-driven design (DDD)** principles: + - Business rules in Domain layer. + - Use `AuditedAggregateRoot`, `FullAuditedEntity`, etc. +- Avoid unnecessary dependencies between layers. ### Testing -- Write unit tests with Jest -- Mock dependencies properly -- Test both success and error scenarios -- Aim for good coverage - -### Performance -- Use OnPush change detection -- Lazy load feature modules -- Implement trackBy for lists -- Use production builds -- Avoid memory leaks +- Use **xUnit**, **Shouldly**, and **NSubstitute** for testing. +- Write **unit and integration tests** per module (`Application.Tests`, `Domain.Tests`). +- Mock dependencies properly and use ABP’s test base classes. ### Security -- Sanitize user inputs -- Use Angular's built-in XSS protection -- Validate on client and server -- Follow ABP's security patterns -- Never expose sensitive data - -## File Structure (Nx Workspace) -``` -libs/ - feature-name/ - src/ - lib/ - components/ - services/ - models/ - state/ - guards/ -``` - -## Common Patterns - -### Smart/Dumb Components -```typescript -// Smart (Container) -@Component({ - template: ` - - - ` -}) -export class ContainerComponent { - items$ = this.store.select(getItems); - constructor(private store: Store) {} -} - -// Dumb (Presentational) -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class ListComponent { - @Input() items: Item[] = []; - @Output() itemSelected = new EventEmitter(); -} -``` - -### Reactive Forms -```typescript -form = this.fb.group({ - name: ['', [Validators.required, Validators.minLength(3)]], - email: ['', [Validators.required, Validators.email]] -}); -``` - -## Best Practices Checklist -✅ OnPush change detection -✅ Proper unsubscription -✅ Async pipe in templates -✅ TypeScript strict typing -✅ Error handling -✅ Unit tests -✅ Localization (no hardcoded strings) -✅ Permission checks -✅ Accessibility attributes -✅ Performance optimization - -## Anti-Patterns to Avoid -❌ Using `any` type -❌ Manual subscriptions without cleanup -❌ Logic in templates -❌ Nested subscriptions -❌ Mutating state directly -❌ Missing error handling -❌ Hardcoded strings - -## Resources -- Angular Style Guide: https://angular.io/guide/styleguide -- ABP Documentation: https://docs.abp.io -- RxJS Operators: https://rxjs.dev/guide/operators - -Always prioritize code maintainability, readability, and following Angular and ABP Framework best practices. +- Use **OpenIddict** for authentication & authorization. +- Implement permission checks through ABP’s infrastructure. +- Enforce **HTTPS** and properly configure **CORS**. + +### API Documentation +- Use **Swagger / OpenAPI** (Swashbuckle or NSwag). +- Add XML comments to controllers and DTOs. +- Follow ABP’s documentation conventions for module APIs. + +**Reference Best Practices:** +- [Domain Services](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services) +- [Repositories](https://abp.io/docs/latest/framework/architecture/best-practices/repositories) +- [Entities](https://abp.io/docs/latest/framework/architecture/best-practices/entities) +- [Application Services](https://abp.io/docs/latest/framework/architecture/best-practices/application-services) +- [DTOs](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects) +- [Entity Framework Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration) + +--- + +## 🌐 3. Angular / TypeScript Development Rules + +### TypeScript Best Practices +- Enable **strict type checking** in `tsconfig.json`. +- Use **type inference** when the type is obvious. +- Avoid `any`; use `unknown` or generics instead. +- Use interfaces and types for clarity and structure. + +### Angular Best Practices +- Prefer **standalone components** (no `NgModules`). +- Do **NOT** set `standalone: true` manually — it’s default. +- Use **signals** for state management. +- Implement **lazy loading** for feature routes. +- Avoid `@HostBinding` / `@HostListener`; use `host` object in decorators. +- Use **`NgOptimizedImage`** for static images (not base64). + +### Components +- Keep components small, focused, and reusable. +- Use `input()` and `output()` functions instead of decorators. +- Use `computed()` for derived state. +- Always set `changeDetection: ChangeDetectionStrategy.OnPush`. +- Use **inline templates** for small components. +- Prefer **Reactive Forms** over template-driven forms. +- Avoid `ngClass` → use `[class]` bindings. +- Avoid `ngStyle` → use `[style]` bindings. + +### State Management +- Manage **local component state** with signals. +- Use **`computed()`** for derived data. +- Keep state transformations **pure and predictable**. +- Avoid `mutate()` on signals — use `update()` or `set()`. + +### Templates +- Use **native control flow** (`@if`, `@for`, `@switch`) instead of structural directives. +- Keep templates minimal and declarative. +- Use the **async pipe** for observable bindings. + +### Services +- Design services for **single responsibility**. +- Provide services using `providedIn: 'root'`. +- Use the **`inject()` function** instead of constructor injection. + +--- + +## 🔒 4. Combined Full-Stack Practices +- Ensure backend and frontend follow consistent **DTO contracts** and **naming conventions**. +- Maintain shared models (e.g., via a `contracts` package or OpenAPI generation). +- Version APIs carefully and handle changes in Angular clients. +- Use ABP’s **CORS**, **Swagger**, and **Identity** modules to simplify frontend integration. +- Apply **global error handling** and consistent response wrappers in both layers. +- Monitor performance with tools like **Application Insights**, **ABP auditing**, or **Angular profiler**. + +--- + +## ✅ Summary +This document defines a unified standard for developing **ABP + Angular full-stack applications**, ensuring: +- Code is **modular**, **performant**, and **maintainable**. +- Teams follow **consistent conventions** across backend and frontend. +- Every layer (Domain, Application, UI) is **clean, testable, and scalable**. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md index f7656afa38..a150753322 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md @@ -1,352 +1,156 @@ -# Junie AI Guidelines - Angular & ABP Framework - -## Introduction -You are assisting with an Angular application built on the ABP Framework. Follow these guidelines to generate high-quality, maintainable code that adheres to best practices. - -## Core Principles -1. **Type Safety**: Use TypeScript strict mode, avoid `any` -2. **Performance**: OnPush change detection, lazy loading -3. **Maintainability**: Clean, readable, well-documented code -4. **Security**: Input validation, proper authentication/authorization -5. **Accessibility**: WCAG 2.1 compliance, ARIA attributes - -## Angular Component Guidelines - -### Component Structure -```typescript -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Subject, takeUntil } from 'rxjs'; - -@Component({ - selector: 'app-feature', - standalone: true, - imports: [CommonModule], - templateUrl: './feature.component.html', - styleUrls: ['./feature.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class FeatureComponent implements OnInit, OnDestroy { - private readonly destroy$ = new Subject(); - - ngOnInit(): void { - // Initialization - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} -``` - -### Key Requirements -- ✅ Use OnPush change detection -- ✅ Implement OnDestroy for cleanup -- ✅ Prefer standalone components -- ✅ Use proper TypeScript types -- ✅ Follow smart/dumb component pattern - -## Service Development - -```typescript -@Injectable({ providedIn: 'root' }) -export class DataService { - constructor(private http: HttpClient) {} - - getData(): Observable { - return this.http.get('/api/data').pipe( - retry(2), - catchError(this.handleError), - shareReplay(1) - ); - } - - private handleError(error: HttpErrorResponse): Observable { - console.error('Service error:', error); - return throwError(() => new Error('Operation failed')); - } -} -``` - -## RxJS Best Practices - -### Operator Selection -| Operator | Use Case | Example | -|----------|----------|---------| -| `switchMap` | Search, navigation (cancel previous) | Search input | -| `mergeMap` | Parallel operations | Batch API calls | -| `concatMap` | Sequential operations | Ordered processing | -| `exhaustMap` | Ignore until complete | Form submission | - -### Subscription Management -```typescript -// ✅ BEST: Use async pipe -data$ = this.service.getData(); - -// ✅ GOOD: Use takeUntil -this.service.getData() - .pipe(takeUntil(this.destroy$)) - .subscribe(data => this.handleData(data)); - -// ❌ BAD: No unsubscription -this.service.getData().subscribe(data => this.data = data); -``` - -## ABP Framework Integration - -### Localization -```typescript -// Service injection -constructor(private localization: LocalizationService) {} - -// Usage in component -getTranslation(key: string): string { - return this.localization.instant(`::${key}`); -} - -// Template usage -{{ '::PageTitle' | abpLocalization }} -``` - -### Permission System -```typescript -// Directive in template - - -// Check in component -canEdit(): boolean { - return this.config.getGrantedPolicy('MyApp.Books.Edit'); -} - -// Observable permission -canEdit$ = this.config.getGrantedPolicy$('MyApp.Books.Edit'); -``` - -### API Proxy Services -```typescript -// ✅ DO: Use generated proxy -import { BookService } from '@proxy/books'; - -constructor(private bookService: BookService) {} - -loadBooks(): void { - this.books$ = this.bookService.getList({ maxResultCount: 10 }); -} - -// ❌ DON'T: Manual HTTP calls for ABP APIs -``` - -### State Management (NGXS) -```typescript -// Actions -export class LoadBooks { - static readonly type = '[Books] Load Books'; -} - -// State -@State({ - name: 'books', - defaults: { books: [], loading: false } -}) -@Injectable() -export class BooksState { - constructor(private bookService: BookService) {} - - @Selector() - static books(state: BooksStateModel) { - return state.books; - } - - @Action(LoadBooks) - loadBooks(ctx: StateContext) { - ctx.patchState({ loading: true }); - return this.bookService.getList().pipe( - tap(response => ctx.patchState({ - books: response.items, - loading: false - })) - ); - } -} -``` - -## Forms - -### Reactive Forms -```typescript -export class FormComponent implements OnInit { - form: FormGroup; - - constructor(private fb: FormBuilder) {} - - ngOnInit(): void { - this.form = this.fb.group({ - name: ['', [Validators.required, Validators.maxLength(100)]], - email: ['', [Validators.required, Validators.email]], - age: [null, [Validators.min(0), Validators.max(120)]] - }); - } - - onSubmit(): void { - if (this.form.valid) { - const formData = this.form.getRawValue(); - this.submitData(formData); - } - } -} -``` - -## Template Best Practices - -```html - -
-
-

{{ user.name }}

-

{{ user.email }}

-
-
- - - - - -

{{ '::WelcomeMessage' | abpLocalization }}

-``` - -## Testing - -```typescript -describe('BookService', () => { - let service: BookService; - let httpMock: HttpTestingController; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [BookService] - }); - service = TestBed.inject(BookService); - httpMock = TestBed.inject(HttpTestingController); - }); - - it('should fetch books', () => { - const mockBooks = [{ id: 1, title: 'Test' }]; - - service.getList().subscribe(books => { - expect(books).toEqual(mockBooks); - }); - - const req = httpMock.expectOne('/api/app/books'); - req.flush(mockBooks); - }); -}); -``` - -## Performance Optimization - -### Change Detection -- Use OnPush strategy everywhere possible -- Avoid function calls in templates -- Use pure pipes -- Implement trackBy for lists - -### Lazy Loading -```typescript -const routes: Routes = [ - { - path: 'books', - loadChildren: () => import('./books/books.module') - .then(m => m.BooksModule) - } -]; -``` - -### Bundle Optimization -- Use standalone components -- Implement lazy loading -- Use dynamic imports -- Tree-shake unused code - -## Security Best Practices - -1. **Input Validation**: Always validate user input -2. **Sanitization**: Use DomSanitizer when needed -3. **XSS Prevention**: Leverage Angular's built-in protection -4. **Authentication**: Use ABP's auth system -5. **Authorization**: Check permissions properly -6. **Data Protection**: Never expose sensitive data in client - -## Code Quality Checklist - -Before submitting code, ensure: -- [ ] TypeScript strict mode enabled -- [ ] No `any` types used -- [ ] OnPush change detection applied -- [ ] Proper unsubscription implemented -- [ ] Error handling in place -- [ ] Unit tests written -- [ ] Localization keys used (no hardcoded text) -- [ ] Permission checks added -- [ ] Accessibility attributes included -- [ ] Performance optimized - -## File Organization (Nx Workspace) - -``` -libs/ - feature-name/ - src/ - lib/ - components/ - component-name/ - component-name.component.ts - component-name.component.html - component-name.component.scss - component-name.component.spec.ts - services/ - models/ - state/ - guards/ - pipes/ - directives/ - index.ts (public API) -``` - -## Common Patterns - -### Smart/Dumb Components -- **Smart**: Container with business logic, state management -- **Dumb**: Presentational with @Input/@Output, OnPush - -### Service Layer -- **API Services**: Backend communication -- **Business Services**: Business logic -- **Utility Services**: Helper functions - -## Anti-Patterns to Avoid - -❌ Using `any` type -❌ Forgetting to unsubscribe -❌ Complex logic in templates -❌ Nested subscriptions -❌ Direct state mutation -❌ Missing error handling -❌ Hardcoded strings -❌ Skipping unit tests - -## Additional Resources - -- Angular Style Guide: https://angular.io/guide/styleguide -- ABP Framework Docs: https://docs.abp.io -- RxJS Documentation: https://rxjs.dev -- Nx Documentation: https://nx.dev -- NGXS Documentation: https://www.ngxs.io - -Follow these guidelines consistently to produce high-quality, maintainable Angular applications with ABP Framework. +# 💻 ABP Full-Stack Development Rules +_Expert Guidelines for .NET Backend (ABP) and Angular Frontend Development_ + +You are a **senior full-stack developer** specializing in **ABP Framework (.NET)** and **Angular (TypeScript)**. +You write **clean, maintainable, and modular** code following **ABP, ASP.NET Core, and Angular best practices**. + +--- + +## 🧩 1. General Principles +- Maintain a clear separation between backend (ABP/.NET) and frontend (Angular) layers. +- Follow **modular architecture** — each layer or feature should be independently testable and reusable. +- Always adhere to **official ABP documentation** ([docs.abp.io](https://docs.abp.io)) and **Angular official guides**. +- Prioritize **readability, maintainability, and performance**. +- Write **idiomatic** and **self-documenting** code. + +--- + +## ⚙️ 2. ABP / .NET Development Rules + +### Code Style and Structure +- Follow ABP’s standard folder structure: + - `*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi` +- Write concise, idiomatic C# code using modern language features. +- Apply **modular and layered design** (Domain, Application, Infrastructure, UI). +- Prefer **LINQ** and **lambda expressions** for collection operations. +- Use **descriptive method and variable names** (`GetActiveUsers`, `CalculateTotalAmount`). + +### Naming Conventions +- **PascalCase** → Classes, Methods, Properties +- **camelCase** → Local variables and private fields +- **UPPER_CASE** → Constants +- Prefix interfaces with **`I`** (e.g., `IUserRepository`). + +### C# and .NET Usage +- Use **C# 10+ features** (records, pattern matching, null-coalescing assignment). +- Utilize **ABP modules** (Permission Management, Setting Management, Audit Logging). +- Integrate **Entity Framework Core** with ABP’s repository abstractions. + +### Syntax and Formatting +- Follow [Microsoft C# Coding Conventions](https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions). +- Use `var` when the type is clear. +- Use `string interpolation` and null-conditional operators. +- Keep code consistent and well-formatted. + +### Error Handling and Validation +- Use exceptions only for exceptional cases. +- Log errors via ABP’s built-in logging or a compatible provider. +- Validate models with **DataAnnotations** or **FluentValidation**. +- Rely on ABP’s global exception middleware for unified responses. +- Return consistent HTTP status codes and error DTOs. + +### API Design +- Build RESTful APIs via `HttpApi` layer and **ABP conventional controllers**. +- Use **attribute-based routing** and versioning when needed. +- Apply **action filters/middleware** for cross-cutting concerns (auditing, authorization). + +### Performance Optimization +- Use `async/await` for I/O operations. +- Use `IDistributedCache` over `IMemoryCache`. +- Avoid N+1 queries — include relations explicitly. +- Implement pagination with `PagedResultDto`. + +### Key Conventions +- Use **Dependency Injection** via ABP’s DI system. +- Apply **repository pattern** or EF Core directly as needed. +- Use **AutoMapper** or ABP object mapping for DTOs. +- Implement **background jobs** with ABP’s job system or `IHostedService`. +- Follow **domain-driven design (DDD)** principles: + - Business rules in Domain layer. + - Use `AuditedAggregateRoot`, `FullAuditedEntity`, etc. +- Avoid unnecessary dependencies between layers. + +### Testing +- Use **xUnit**, **Shouldly**, and **NSubstitute** for testing. +- Write **unit and integration tests** per module (`Application.Tests`, `Domain.Tests`). +- Mock dependencies properly and use ABP’s test base classes. + +### Security +- Use **OpenIddict** for authentication & authorization. +- Implement permission checks through ABP’s infrastructure. +- Enforce **HTTPS** and properly configure **CORS**. + +### API Documentation +- Use **Swagger / OpenAPI** (Swashbuckle or NSwag). +- Add XML comments to controllers and DTOs. +- Follow ABP’s documentation conventions for module APIs. + +**Reference Best Practices:** +- [Domain Services](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services) +- [Repositories](https://abp.io/docs/latest/framework/architecture/best-practices/repositories) +- [Entities](https://abp.io/docs/latest/framework/architecture/best-practices/entities) +- [Application Services](https://abp.io/docs/latest/framework/architecture/best-practices/application-services) +- [DTOs](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects) +- [Entity Framework Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration) + +--- + +## 🌐 3. Angular / TypeScript Development Rules + +### TypeScript Best Practices +- Enable **strict type checking** in `tsconfig.json`. +- Use **type inference** when the type is obvious. +- Avoid `any`; use `unknown` or generics instead. +- Use interfaces and types for clarity and structure. + +### Angular Best Practices +- Prefer **standalone components** (no `NgModules`). +- Do **NOT** set `standalone: true` manually — it’s default. +- Use **signals** for state management. +- Implement **lazy loading** for feature routes. +- Avoid `@HostBinding` / `@HostListener`; use `host` object in decorators. +- Use **`NgOptimizedImage`** for static images (not base64). + +### Components +- Keep components small, focused, and reusable. +- Use `input()` and `output()` functions instead of decorators. +- Use `computed()` for derived state. +- Always set `changeDetection: ChangeDetectionStrategy.OnPush`. +- Use **inline templates** for small components. +- Prefer **Reactive Forms** over template-driven forms. +- Avoid `ngClass` → use `[class]` bindings. +- Avoid `ngStyle` → use `[style]` bindings. + +### State Management +- Manage **local component state** with signals. +- Use **`computed()`** for derived data. +- Keep state transformations **pure and predictable**. +- Avoid `mutate()` on signals — use `update()` or `set()`. + +### Templates +- Use **native control flow** (`@if`, `@for`, `@switch`) instead of structural directives. +- Keep templates minimal and declarative. +- Use the **async pipe** for observable bindings. + +### Services +- Design services for **single responsibility**. +- Provide services using `providedIn: 'root'`. +- Use the **`inject()` function** instead of constructor injection. + +--- + +## 🔒 4. Combined Full-Stack Practices +- Ensure backend and frontend follow consistent **DTO contracts** and **naming conventions**. +- Maintain shared models (e.g., via a `contracts` package or OpenAPI generation). +- Version APIs carefully and handle changes in Angular clients. +- Use ABP’s **CORS**, **Swagger**, and **Identity** modules to simplify frontend integration. +- Apply **global error handling** and consistent response wrappers in both layers. +- Monitor performance with tools like **Application Insights**, **ABP auditing**, or **Angular profiler**. + +--- + +## ✅ Summary +This document defines a unified standard for developing **ABP + Angular full-stack applications**, ensuring: +- Code is **modular**, **performant**, and **maintainable**. +- Teams follow **consistent conventions** across backend and frontend. +- Every layer (Domain, Application, UI) is **clean, testable, and scalable**. diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md index 7449f4f582..a150753322 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md @@ -1,652 +1,156 @@ -# Windsurf AI Development Guidelines - Angular & ABP Framework +# 💻 ABP Full-Stack Development Rules +_Expert Guidelines for .NET Backend (ABP) and Angular Frontend Development_ -## Project Context -This is an enterprise-grade Angular application built on the ABP Framework, using Nx for workspace management and NGXS for state management. Follow these comprehensive guidelines to generate production-ready code. +You are a **senior full-stack developer** specializing in **ABP Framework (.NET)** and **Angular (TypeScript)**. +You write **clean, maintainable, and modular** code following **ABP, ASP.NET Core, and Angular best practices**. --- -## 🎯 Core Development Principles - -### 1. Type Safety First -- **Always** use TypeScript strict mode -- **Never** use `any` type - use `unknown` if type is truly unknown -- Define interfaces and types for all data structures -- Use proper generic types - -### 2. Performance Optimization -- Use OnPush change detection strategy by default -- Implement lazy loading for feature modules -- Use trackBy with *ngFor directives -- Leverage async pipe for observables -- Avoid memory leaks with proper cleanup - -### 3. Code Maintainability -- Follow SOLID principles -- Write self-documenting code with clear naming -- Keep functions small (<20 lines ideally) -- Add JSDoc comments for complex logic -- Use meaningful variable and function names - -### 4. Security -- Validate all user inputs -- Sanitize data when necessary (DomSanitizer) -- Use ABP's permission system -- Never expose sensitive data in client code -- Follow OWASP security guidelines - -### 5. Accessibility -- Include ARIA attributes -- Support keyboard navigation -- Use semantic HTML -- Follow WCAG 2.1 AA standards - ---- - -## 📦 Angular Component Architecture - -### Standard Component Structure - -```typescript -import { - ChangeDetectionStrategy, - Component, - OnDestroy, - OnInit, - inject -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Subject, takeUntil } from 'rxjs'; - -@Component({ - selector: 'app-feature-name', - standalone: true, - imports: [CommonModule], - templateUrl: './feature-name.component.html', - styleUrls: ['./feature-name.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class FeatureNameComponent implements OnInit, OnDestroy { - // Use inject() function (Angular 14+) - private readonly dataService = inject(DataService); - private readonly destroy$ = new Subject(); - - // Observable streams with $ suffix - data$ = this.dataService.getData(); - - ngOnInit(): void { - // Initialization logic - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} -``` - -### Component Best Practices -✅ **DO:** -- Use OnPush change detection -- Implement OnDestroy for cleanup -- Use standalone components for new code -- Prefer async pipe over manual subscriptions -- Use readonly for immutable properties -- Use inject() function for dependency injection - -❌ **DON'T:** -- Put business logic in components -- Mutate @Input() properties -- Forget to unsubscribe from observables -- Use function calls in templates -- Use nested subscriptions - ---- - -## 🔧 Service Development - -### Service Pattern - -```typescript -import { Injectable, inject } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable, catchError, retry, shareReplay, throwError } from 'rxjs'; - -@Injectable({ providedIn: 'root' }) -export class DataService { - private readonly http = inject(HttpClient); - private readonly cache$ = new Map>(); - - getData(id: string): Observable { - // Implement caching - if (!this.cache$.has(id)) { - this.cache$.set( - id, - this.http.get(`/api/data/${id}`).pipe( - retry(2), - catchError(this.handleError), - shareReplay(1) - ) - ); - } - return this.cache$.get(id)!; - } - - private handleError(error: HttpErrorResponse): Observable { - console.error('Service error:', error); - // Log to monitoring service here - return throwError(() => new Error('Operation failed. Please try again.')); - } -} -``` - -### Service Best Practices -- Use `providedIn: 'root'` for singleton services -- Return Observables for async operations -- Implement proper error handling -- Use caching strategies when appropriate -- Keep services focused (Single Responsibility) - ---- - -## 🌊 RxJS Patterns & Operators - -### Operator Decision Matrix - -| Operator | Use Case | Behavior | -|----------|----------|----------| -| **switchMap** | Search, navigation | Cancels previous, emits latest | -| **mergeMap** | Parallel operations | Runs all concurrently | -| **concatMap** | Sequential operations | Maintains order, waits for completion | -| **exhaustMap** | Form submission, clicks | Ignores new until current completes | - -### Subscription Management - -```typescript -export class ExampleComponent implements OnDestroy { - private readonly destroy$ = new Subject(); - - ngOnInit(): void { - // Pattern 1: takeUntil - this.service.getData() - .pipe(takeUntil(this.destroy$)) - .subscribe(data => this.handleData(data)); - - // Pattern 2: take(1) for single emission - this.service.getConfig() - .pipe(take(1)) - .subscribe(config => this.config = config); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} -``` - -### Template Usage (Preferred) - -```typescript -// Component -data$ = this.service.getData().pipe( - catchError(error => { - this.handleError(error); - return of([]); - }) -); - -// Template -
- {{ data.name }} -
-``` - ---- - -## 🏗️ ABP Framework Integration - -### 1. Localization System - -```typescript -// Component -import { LocalizationService } from '@abp/ng.core'; - -export class MyComponent { - private readonly localization = inject(LocalizationService); - - readonly texts = { - title: this.localization.instant('::PageTitle'), - save: this.localization.instant('::Save'), - cancel: this.localization.instant('::Cancel') - }; -} - -// Template -

{{ '::PageTitle' | abpLocalization }}

- -``` - -### 2. Permission System - -```typescript -// Template - - -// Component -import { ConfigStateService } from '@abp/ng.core'; - -export class BookListComponent { - private readonly config = inject(ConfigStateService); - - canEdit$ = this.config.getGrantedPolicy$('BookStore.Books.Edit'); - canDelete$ = this.config.getGrantedPolicy$('BookStore.Books.Delete'); - - checkPermission(): boolean { - return this.config.getGrantedPolicy('BookStore.Books.Create'); - } -} -``` - -### 3. API Proxy Services - -```typescript -// ✅ ALWAYS use generated proxy services -import { BookService } from '@proxy/books'; -import { GetBooksInput } from '@proxy/books/models'; - -export class BookListComponent { - private readonly bookService = inject(BookService); - - books$ = this.bookService.getList({ - maxResultCount: 10, - skipCount: 0 - }); - - createBook(input: CreateBookDto): void { - this.bookService.create(input).pipe( - take(1), - catchError(this.handleError) - ).subscribe(() => this.refreshList()); - } -} - -// ❌ DON'T create manual HTTP calls for ABP APIs -``` - -### 4. State Management with NGXS - -```typescript -// Actions -export class GetBooks { - static readonly type = '[Books] Get Books'; - constructor(public payload: GetBooksInput) {} -} - -export class CreateBook { - static readonly type = '[Books] Create Book'; - constructor(public payload: CreateBookDto) {} -} - -// State -export interface BooksStateModel { - books: BookDto[]; - loading: boolean; - error: string | null; - totalCount: number; -} - -@State({ - name: 'books', - defaults: { - books: [], - loading: false, - error: null, - totalCount: 0 - } -}) -@Injectable() -export class BooksState { - private readonly bookService = inject(BookService); - - @Selector() - static books(state: BooksStateModel): BookDto[] { - return state.books; - } - - @Selector() - static loading(state: BooksStateModel): boolean { - return state.loading; - } - - @Selector() - static totalCount(state: BooksStateModel): number { - return state.totalCount; - } - - @Action(GetBooks) - getBooks(ctx: StateContext, action: GetBooks) { - ctx.patchState({ loading: true, error: null }); - - return this.bookService.getList(action.payload).pipe( - tap(response => { - ctx.patchState({ - books: response.items, - totalCount: response.totalCount, - loading: false - }); - }), - catchError(error => { - ctx.patchState({ - loading: false, - error: error.message - }); - return throwError(() => error); - }) - ); - } - - @Action(CreateBook) - createBook(ctx: StateContext, action: CreateBook) { - return this.bookService.create(action.payload).pipe( - tap(book => { - const state = ctx.getState(); - ctx.patchState({ - books: [...state.books, book], - totalCount: state.totalCount + 1 - }); - }) - ); - } -} -``` - -### 5. Multi-Tenancy Support - -```typescript -import { ConfigStateService } from '@abp/ng.core'; - -export class TenantAwareComponent { - private readonly config = inject(ConfigStateService); - - get currentTenant() { - return this.config.getOne('currentTenant'); - } - - get isTenantContext(): boolean { - return !!this.currentTenant?.id; - } -} -``` - ---- - -## 📝 Reactive Forms - -### Form Implementation - -```typescript -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { CustomValidators } from './validators'; - -export class BookFormComponent implements OnInit { - private readonly fb = inject(FormBuilder); - - bookForm!: FormGroup; - - ngOnInit(): void { - this.bookForm = this.fb.group({ - name: ['', [ - Validators.required, - Validators.minLength(3), - Validators.maxLength(128) - ]], - type: ['', Validators.required], - publishDate: ['', [ - Validators.required, - CustomValidators.notFutureDate - ]], - price: [0, [ - Validators.required, - Validators.min(0), - Validators.max(999999.99) - ]], - description: ['', Validators.maxLength(1000)] - }); - } - - onSubmit(): void { - if (this.bookForm.valid) { - const formValue = this.bookForm.getRawValue(); - this.submitForm(formValue); - } else { - this.markFormGroupTouched(this.bookForm); - } - } - - private markFormGroupTouched(formGroup: FormGroup): void { - Object.keys(formGroup.controls).forEach(key => { - const control = formGroup.get(key); - control?.markAsTouched(); - - if (control instanceof FormGroup) { - this.markFormGroupTouched(control); - } - }); - } -} -``` - ---- - -## 🎨 Template Best Practices - -```html - -
-
-

{{ book.name }}

-

{{ book.publishDate | date:'shortDate' }}

-

{{ book.price | currency }}

-
-
- - -
Loading...
-
- - - - - -
- -
- - -

{{ '::BookManagement' | abpLocalization }}

-

{{ '::BookDescription' | abpLocalization:{ name: book.name } }}

-``` +## 🧩 1. General Principles +- Maintain a clear separation between backend (ABP/.NET) and frontend (Angular) layers. +- Follow **modular architecture** — each layer or feature should be independently testable and reusable. +- Always adhere to **official ABP documentation** ([docs.abp.io](https://docs.abp.io)) and **Angular official guides**. +- Prioritize **readability, maintainability, and performance**. +- Write **idiomatic** and **self-documenting** code. --- -## 🧪 Testing Strategies - -### Component Testing - -```typescript -describe('BookListComponent', () => { - let component: BookListComponent; - let fixture: ComponentFixture; - let mockBookService: jasmine.SpyObj; - - beforeEach(async () => { - mockBookService = jasmine.createSpyObj('BookService', [ - 'getList', - 'create', - 'delete' - ]); - - await TestBed.configureTestingModule({ - imports: [BookListComponent], - providers: [ - { provide: BookService, useValue: mockBookService } - ] - }).compileComponents(); - - fixture = TestBed.createComponent(BookListComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should load books on init', () => { - const mockBooks = [ - { id: '1', name: 'Book 1', price: 10 }, - { id: '2', name: 'Book 2', price: 20 } - ]; - - mockBookService.getList.and.returnValue(of({ - items: mockBooks, - totalCount: 2 - })); - - component.ngOnInit(); - - expect(mockBookService.getList).toHaveBeenCalled(); - }); -}); -``` +## ⚙️ 2. ABP / .NET Development Rules + +### Code Style and Structure +- Follow ABP’s standard folder structure: + - `*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi` +- Write concise, idiomatic C# code using modern language features. +- Apply **modular and layered design** (Domain, Application, Infrastructure, UI). +- Prefer **LINQ** and **lambda expressions** for collection operations. +- Use **descriptive method and variable names** (`GetActiveUsers`, `CalculateTotalAmount`). + +### Naming Conventions +- **PascalCase** → Classes, Methods, Properties +- **camelCase** → Local variables and private fields +- **UPPER_CASE** → Constants +- Prefix interfaces with **`I`** (e.g., `IUserRepository`). + +### C# and .NET Usage +- Use **C# 10+ features** (records, pattern matching, null-coalescing assignment). +- Utilize **ABP modules** (Permission Management, Setting Management, Audit Logging). +- Integrate **Entity Framework Core** with ABP’s repository abstractions. + +### Syntax and Formatting +- Follow [Microsoft C# Coding Conventions](https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions). +- Use `var` when the type is clear. +- Use `string interpolation` and null-conditional operators. +- Keep code consistent and well-formatted. + +### Error Handling and Validation +- Use exceptions only for exceptional cases. +- Log errors via ABP’s built-in logging or a compatible provider. +- Validate models with **DataAnnotations** or **FluentValidation**. +- Rely on ABP’s global exception middleware for unified responses. +- Return consistent HTTP status codes and error DTOs. + +### API Design +- Build RESTful APIs via `HttpApi` layer and **ABP conventional controllers**. +- Use **attribute-based routing** and versioning when needed. +- Apply **action filters/middleware** for cross-cutting concerns (auditing, authorization). + +### Performance Optimization +- Use `async/await` for I/O operations. +- Use `IDistributedCache` over `IMemoryCache`. +- Avoid N+1 queries — include relations explicitly. +- Implement pagination with `PagedResultDto`. + +### Key Conventions +- Use **Dependency Injection** via ABP’s DI system. +- Apply **repository pattern** or EF Core directly as needed. +- Use **AutoMapper** or ABP object mapping for DTOs. +- Implement **background jobs** with ABP’s job system or `IHostedService`. +- Follow **domain-driven design (DDD)** principles: + - Business rules in Domain layer. + - Use `AuditedAggregateRoot`, `FullAuditedEntity`, etc. +- Avoid unnecessary dependencies between layers. + +### Testing +- Use **xUnit**, **Shouldly**, and **NSubstitute** for testing. +- Write **unit and integration tests** per module (`Application.Tests`, `Domain.Tests`). +- Mock dependencies properly and use ABP’s test base classes. + +### Security +- Use **OpenIddict** for authentication & authorization. +- Implement permission checks through ABP’s infrastructure. +- Enforce **HTTPS** and properly configure **CORS**. + +### API Documentation +- Use **Swagger / OpenAPI** (Swashbuckle or NSwag). +- Add XML comments to controllers and DTOs. +- Follow ABP’s documentation conventions for module APIs. + +**Reference Best Practices:** +- [Domain Services](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services) +- [Repositories](https://abp.io/docs/latest/framework/architecture/best-practices/repositories) +- [Entities](https://abp.io/docs/latest/framework/architecture/best-practices/entities) +- [Application Services](https://abp.io/docs/latest/framework/architecture/best-practices/application-services) +- [DTOs](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects) +- [Entity Framework Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration) --- -## 🚀 Performance Optimization - -### 1. Change Detection Strategy -```typescript -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush -}) -``` - -### 2. TrackBy Functions -```typescript -trackByBookId(index: number, book: BookDto): string { - return book.id; -} -``` - -### 3. Lazy Loading -```typescript -const routes: Routes = [ - { - path: 'books', - loadChildren: () => import('./books/books.routes') - .then(m => m.BOOKS_ROUTES) - } -]; -``` - -### 4. Virtual Scrolling (for large lists) -```typescript - -
- {{ book.name }} -
-
-``` - ---- - -## 📁 File Structure (Nx Workspace) - -``` -libs/ - books/ - feature/ - src/ - lib/ - components/ - book-list/ - book-form/ - book-detail/ - services/ - state/ - guards/ - books-feature.routes.ts - index.ts - data-access/ - src/ - lib/ - services/ - models/ - index.ts - ui/ - src/ - lib/ - components/ - index.ts -``` - ---- - -## ✅ Quality Checklist - -Before committing code, verify: -- [ ] TypeScript strict mode compliance -- [ ] No `any` types -- [ ] OnPush change detection -- [ ] Proper unsubscription -- [ ] Error handling implemented -- [ ] Unit tests written -- [ ] Localization keys used -- [ ] Permission checks added -- [ ] Accessibility attributes included -- [ ] Performance optimized (trackBy, lazy loading) -- [ ] Security validated -- [ ] Code formatted (Prettier) -- [ ] Linting passed +## 🌐 3. Angular / TypeScript Development Rules + +### TypeScript Best Practices +- Enable **strict type checking** in `tsconfig.json`. +- Use **type inference** when the type is obvious. +- Avoid `any`; use `unknown` or generics instead. +- Use interfaces and types for clarity and structure. + +### Angular Best Practices +- Prefer **standalone components** (no `NgModules`). +- Do **NOT** set `standalone: true` manually — it’s default. +- Use **signals** for state management. +- Implement **lazy loading** for feature routes. +- Avoid `@HostBinding` / `@HostListener`; use `host` object in decorators. +- Use **`NgOptimizedImage`** for static images (not base64). + +### Components +- Keep components small, focused, and reusable. +- Use `input()` and `output()` functions instead of decorators. +- Use `computed()` for derived state. +- Always set `changeDetection: ChangeDetectionStrategy.OnPush`. +- Use **inline templates** for small components. +- Prefer **Reactive Forms** over template-driven forms. +- Avoid `ngClass` → use `[class]` bindings. +- Avoid `ngStyle` → use `[style]` bindings. + +### State Management +- Manage **local component state** with signals. +- Use **`computed()`** for derived data. +- Keep state transformations **pure and predictable**. +- Avoid `mutate()` on signals — use `update()` or `set()`. + +### Templates +- Use **native control flow** (`@if`, `@for`, `@switch`) instead of structural directives. +- Keep templates minimal and declarative. +- Use the **async pipe** for observable bindings. + +### Services +- Design services for **single responsibility**. +- Provide services using `providedIn: 'root'`. +- Use the **`inject()` function** instead of constructor injection. --- -## 🚫 Common Anti-Patterns - -| Anti-Pattern | Why It's Bad | Better Approach | -|--------------|--------------|-----------------| -| Using `any` | Loses type safety | Use proper types or `unknown` | -| No unsubscribe | Memory leaks | Use takeUntil or async pipe | -| Logic in templates | Hard to test | Move to component/service | -| Nested subscriptions | Hard to maintain | Use RxJS operators | -| Direct state mutation | Breaks change detection | Use immutable patterns | -| Missing error handling | Poor UX | Always handle errors | -| Hardcoded strings | Not localizable | Use localization system | - ---- - -## 📚 Resources - -- [Angular Style Guide](https://angular.io/guide/styleguide) -- [ABP Framework Documentation](https://docs.abp.io) -- [RxJS Documentation](https://rxjs.dev) -- [Nx Documentation](https://nx.dev) -- [NGXS Documentation](https://www.ngxs.io) +## 🔒 4. Combined Full-Stack Practices +- Ensure backend and frontend follow consistent **DTO contracts** and **naming conventions**. +- Maintain shared models (e.g., via a `contracts` package or OpenAPI generation). +- Version APIs carefully and handle changes in Angular clients. +- Use ABP’s **CORS**, **Swagger**, and **Identity** modules to simplify frontend integration. +- Apply **global error handling** and consistent response wrappers in both layers. +- Monitor performance with tools like **Application Insights**, **ABP auditing**, or **Angular profiler**. --- -Follow these guidelines to build maintainable, performant, and secure Angular applications with ABP Framework. +## ✅ Summary +This document defines a unified standard for developing **ABP + Angular full-stack applications**, ensuring: +- Code is **modular**, **performant**, and **maintainable**. +- Teams follow **consistent conventions** across backend and frontend. +- Every layer (Domain, Application, UI) is **clean, testable, and scalable**. From 73a8beba67761c9fad6cef5e72505af7a1000a74 Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Fri, 10 Oct 2025 09:23:24 +0300 Subject: [PATCH 3/7] Refactor ai-config to use comma-separated tool string Updated the ai-config schematic to accept a comma-separated string for the 'tool' option instead of an array. Adjusted validation, parsing, and schema prompts accordingly for improved CLI usability and flexibility. --- .../src/commands/ai-config/index.ts | 51 ++++++++++--------- .../src/commands/ai-config/model.ts | 2 +- .../src/commands/ai-config/schema.json | 22 ++------ 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts index 10691f6609..5b510af111 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts @@ -3,23 +3,40 @@ import { join, normalize } from '@angular-devkit/core'; import { AiConfigSchema, AiTool } from './model'; import { getWorkspace } from '../../utils'; -/** - * Generates AI configuration files for Angular projects based on selected tools. - * This schematic creates configuration files that help AI tools follow Angular best practices. - */ export default function (options: AiConfigSchema): Rule { return async (tree: Tree) => { - // Validate options - if (!options.tool || options.tool.length === 0) { + if (!options.tool || options.tool.trim() === '') { + console.log('ℹ️ No AI tools selected. Skipping configuration generation.'); + console.log(''); + console.log('💡 Usage examples:'); + console.log(' ng g @abp/ng.schematics:ai-config --tool=claude,cursor'); + console.log(' ng g @abp/ng.schematics:ai-config --tool=gemini --target-project=my-app'); + console.log(''); + console.log('Available tools: claude, copilot, cursor, gemini, junie, windsurf'); + return tree; + } + + const tools = options.tool + .split(/[,\s]+/) + .map(t => t.trim()) + .filter(t => t) as AiTool[]; + + const validTools: AiTool[] = ['claude', 'copilot', 'cursor', 'gemini', 'junie', 'windsurf']; + const invalidTools = tools.filter(tool => !validTools.includes(tool)); + if (invalidTools.length > 0) { + throw new SchematicsException( + `Invalid AI tool(s): ${invalidTools.join(', ')}. Valid options are: ${validTools.join(', ')}` + ); + } + + if (tools.length === 0) { console.log('ℹ️ No AI tools selected. Skipping configuration generation.'); return tree; } - // Get workspace configuration const workspace = await getWorkspace(tree); let targetPath = '/'; - // If targetProject is specified, generate in project directory if (options.targetProject) { const project = workspace.projects.get(options.targetProject); if (!project) { @@ -32,20 +49,18 @@ export default function (options: AiConfigSchema): Rule { console.log('🚀 Generating AI configuration files...'); console.log(`📁 Target path: ${targetPath}`); - console.log(`🤖 Selected tools: ${options.tool.join(', ')}`); + console.log(`🤖 Selected tools: ${tools.join(', ')}`); - // Generate rules for each selected tool - const rules: Rule[] = options.tool + const rules: Rule[] = tools .map(tool => generateConfigForTool(tool, targetPath, options.overwrite || false)); - // Apply all rules and log results return chain([ ...rules, (tree: Tree) => { console.log('✅ AI configuration files generated successfully!'); console.log('\n📝 Generated files:'); - options.tool.forEach(tool => { + tools.forEach(tool => { const configPath = getConfigPath(tool, targetPath); console.log(` - ${configPath}`); }); @@ -58,37 +73,27 @@ export default function (options: AiConfigSchema): Rule { }; } -/** - * Generates configuration for a specific AI tool - */ function generateConfigForTool(tool: AiTool, targetPath: string, overwrite: boolean): Rule { return (tree: Tree) => { const configPath = getConfigPath(tool, targetPath); - // Check if file already exists if (tree.exists(configPath) && !overwrite) { console.log(`⚠️ Configuration file already exists: ${configPath}`); console.log(` Use --overwrite flag to replace existing files.`); return tree; } - // Get template source const sourceDir = `./files/${tool}`; const source = apply(url(sourceDir), [ filter(path => { - // Filter out any unnecessary files return !path.endsWith('.DS_Store'); }) ]); - // Merge with existing tree return mergeWith(source, overwrite ? MergeStrategy.Overwrite : MergeStrategy.Default); }; } -/** - * Gets the configuration file path for a specific tool - */ function getConfigPath(tool: AiTool, basePath: string): string { const configFiles: Record = { claude: '.claude/CLAUDE.md', diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts index 8a10f85509..ab871971c2 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts @@ -1,7 +1,7 @@ export type AiTool = 'claude' | 'copilot' | 'cursor' | 'gemini' | 'junie' | 'windsurf'; export interface AiConfigSchema { - tool: AiTool[]; + tool?: string; targetProject?: string; overwrite?: boolean; } diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json b/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json index 7ad18a1fe4..0d5e0117ed 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json @@ -5,25 +5,11 @@ "type": "object", "properties": { "tool": { - "description": "Specifies which AI tools to generate configuration files for", - "type": "array", - "items": { - "type": "string", - "enum": ["claude", "copilot", "cursor", "gemini", "junie", "windsurf"] - }, - "default": [], + "description": "Comma-separated list of AI tools (e.g., claude,cursor,gemini)", + "type": "string", "x-prompt": { - "message": "Which AI tools would you like to generate configuration files for?", - "type": "list", - "multiselect": true, - "items": [ - { "value": "claude", "label": "Claude (Anthropic) - AI assistant with deep code understanding" }, - { "value": "copilot", "label": "GitHub Copilot - AI pair programmer" }, - { "value": "cursor", "label": "Cursor - AI-first code editor" }, - { "value": "gemini", "label": "Google Gemini - AI assistant by Google" }, - { "value": "junie", "label": "Junie AI - Development assistant" }, - { "value": "windsurf", "label": "Windsurf - AI development environment" } - ] + "message": "Which AI tools would you like to generate configuration files for? (comma-separated)", + "type": "input" } }, "targetProject": { From 8c75745f87b6d5c0a51a2a55445c989e18ddd259 Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Fri, 10 Oct 2025 12:12:08 +0300 Subject: [PATCH 4/7] Add ABP Angular component replacement documentation Expanded guidelines in multiple AI config instruction files to include details and examples for using ABP Angular's ReplaceableComponentsService. This addition covers key features, usage, important notes, and links to full documentation for customizing and replacing default components, layouts, and UI elements. --- .../ai-config/files/claude/.claude/CLAUDE.md | 30 +++++++++++++++++++ .../copilot/.github/copilot-instructions.md | 30 +++++++++++++++++++ .../files/cursor/.cursor/rules/cursor.mdc | 30 +++++++++++++++++++ .../ai-config/files/gemini/.gemini/GEMINI.md | 30 +++++++++++++++++++ .../files/junie/.junie/guidelines.md | 30 +++++++++++++++++++ .../windsurf/.windsurf/rules/guidelines.md | 30 +++++++++++++++++++ 6 files changed, 180 insertions(+) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md index a150753322..32dfab0275 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/claude/.claude/CLAUDE.md @@ -137,6 +137,36 @@ You write **clean, maintainable, and modular** code following **ABP, ASP.NET Cor - Provide services using `providedIn: 'root'`. - Use the **`inject()` function** instead of constructor injection. +### Component Replacement +ABP Angular provides a powerful **component replacement** system via `ReplaceableComponentsService`: + +**Key Features:** +- Replace ABP default components (Roles, Users, Tenants, etc.) with custom implementations +- Replace layouts (Application, Account, Empty) +- Replace UI elements (Logo, Routes, NavItems) + +**Basic Usage:** +```typescript +import { ReplaceableComponentsService } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; + +constructor(private replaceableComponents: ReplaceableComponentsService) { + this.replaceableComponents.add({ + component: YourCustomComponent, + key: eIdentityComponents.Roles, + }); +} +``` + +**Important Notes:** +- Component templates must include `` for layouts +- Use the second parameter as `true` for runtime replacement (refreshes route) +- Runtime replacement clears component state and re-runs initialization logic + +**📚 Full Documentation:** +For detailed examples, layout replacement, and advanced scenarios: +[Component Replacement Guide](https://abp.io/docs/latest/framework/ui/angular/customization-user-interface) + --- ## 🔒 4. Combined Full-Stack Practices diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md index a150753322..32dfab0275 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/copilot/.github/copilot-instructions.md @@ -137,6 +137,36 @@ You write **clean, maintainable, and modular** code following **ABP, ASP.NET Cor - Provide services using `providedIn: 'root'`. - Use the **`inject()` function** instead of constructor injection. +### Component Replacement +ABP Angular provides a powerful **component replacement** system via `ReplaceableComponentsService`: + +**Key Features:** +- Replace ABP default components (Roles, Users, Tenants, etc.) with custom implementations +- Replace layouts (Application, Account, Empty) +- Replace UI elements (Logo, Routes, NavItems) + +**Basic Usage:** +```typescript +import { ReplaceableComponentsService } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; + +constructor(private replaceableComponents: ReplaceableComponentsService) { + this.replaceableComponents.add({ + component: YourCustomComponent, + key: eIdentityComponents.Roles, + }); +} +``` + +**Important Notes:** +- Component templates must include `` for layouts +- Use the second parameter as `true` for runtime replacement (refreshes route) +- Runtime replacement clears component state and re-runs initialization logic + +**📚 Full Documentation:** +For detailed examples, layout replacement, and advanced scenarios: +[Component Replacement Guide](https://abp.io/docs/latest/framework/ui/angular/customization-user-interface) + --- ## 🔒 4. Combined Full-Stack Practices diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc index a150753322..32dfab0275 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/cursor/.cursor/rules/cursor.mdc @@ -137,6 +137,36 @@ You write **clean, maintainable, and modular** code following **ABP, ASP.NET Cor - Provide services using `providedIn: 'root'`. - Use the **`inject()` function** instead of constructor injection. +### Component Replacement +ABP Angular provides a powerful **component replacement** system via `ReplaceableComponentsService`: + +**Key Features:** +- Replace ABP default components (Roles, Users, Tenants, etc.) with custom implementations +- Replace layouts (Application, Account, Empty) +- Replace UI elements (Logo, Routes, NavItems) + +**Basic Usage:** +```typescript +import { ReplaceableComponentsService } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; + +constructor(private replaceableComponents: ReplaceableComponentsService) { + this.replaceableComponents.add({ + component: YourCustomComponent, + key: eIdentityComponents.Roles, + }); +} +``` + +**Important Notes:** +- Component templates must include `` for layouts +- Use the second parameter as `true` for runtime replacement (refreshes route) +- Runtime replacement clears component state and re-runs initialization logic + +**📚 Full Documentation:** +For detailed examples, layout replacement, and advanced scenarios: +[Component Replacement Guide](https://abp.io/docs/latest/framework/ui/angular/customization-user-interface) + --- ## 🔒 4. Combined Full-Stack Practices diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md index a150753322..32dfab0275 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/gemini/.gemini/GEMINI.md @@ -137,6 +137,36 @@ You write **clean, maintainable, and modular** code following **ABP, ASP.NET Cor - Provide services using `providedIn: 'root'`. - Use the **`inject()` function** instead of constructor injection. +### Component Replacement +ABP Angular provides a powerful **component replacement** system via `ReplaceableComponentsService`: + +**Key Features:** +- Replace ABP default components (Roles, Users, Tenants, etc.) with custom implementations +- Replace layouts (Application, Account, Empty) +- Replace UI elements (Logo, Routes, NavItems) + +**Basic Usage:** +```typescript +import { ReplaceableComponentsService } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; + +constructor(private replaceableComponents: ReplaceableComponentsService) { + this.replaceableComponents.add({ + component: YourCustomComponent, + key: eIdentityComponents.Roles, + }); +} +``` + +**Important Notes:** +- Component templates must include `` for layouts +- Use the second parameter as `true` for runtime replacement (refreshes route) +- Runtime replacement clears component state and re-runs initialization logic + +**📚 Full Documentation:** +For detailed examples, layout replacement, and advanced scenarios: +[Component Replacement Guide](https://abp.io/docs/latest/framework/ui/angular/customization-user-interface) + --- ## 🔒 4. Combined Full-Stack Practices diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md index a150753322..32dfab0275 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/junie/.junie/guidelines.md @@ -137,6 +137,36 @@ You write **clean, maintainable, and modular** code following **ABP, ASP.NET Cor - Provide services using `providedIn: 'root'`. - Use the **`inject()` function** instead of constructor injection. +### Component Replacement +ABP Angular provides a powerful **component replacement** system via `ReplaceableComponentsService`: + +**Key Features:** +- Replace ABP default components (Roles, Users, Tenants, etc.) with custom implementations +- Replace layouts (Application, Account, Empty) +- Replace UI elements (Logo, Routes, NavItems) + +**Basic Usage:** +```typescript +import { ReplaceableComponentsService } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; + +constructor(private replaceableComponents: ReplaceableComponentsService) { + this.replaceableComponents.add({ + component: YourCustomComponent, + key: eIdentityComponents.Roles, + }); +} +``` + +**Important Notes:** +- Component templates must include `` for layouts +- Use the second parameter as `true` for runtime replacement (refreshes route) +- Runtime replacement clears component state and re-runs initialization logic + +**📚 Full Documentation:** +For detailed examples, layout replacement, and advanced scenarios: +[Component Replacement Guide](https://abp.io/docs/latest/framework/ui/angular/customization-user-interface) + --- ## 🔒 4. Combined Full-Stack Practices diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md index a150753322..32dfab0275 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/files/windsurf/.windsurf/rules/guidelines.md @@ -137,6 +137,36 @@ You write **clean, maintainable, and modular** code following **ABP, ASP.NET Cor - Provide services using `providedIn: 'root'`. - Use the **`inject()` function** instead of constructor injection. +### Component Replacement +ABP Angular provides a powerful **component replacement** system via `ReplaceableComponentsService`: + +**Key Features:** +- Replace ABP default components (Roles, Users, Tenants, etc.) with custom implementations +- Replace layouts (Application, Account, Empty) +- Replace UI elements (Logo, Routes, NavItems) + +**Basic Usage:** +```typescript +import { ReplaceableComponentsService } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; + +constructor(private replaceableComponents: ReplaceableComponentsService) { + this.replaceableComponents.add({ + component: YourCustomComponent, + key: eIdentityComponents.Roles, + }); +} +``` + +**Important Notes:** +- Component templates must include `` for layouts +- Use the second parameter as `true` for runtime replacement (refreshes route) +- Runtime replacement clears component state and re-runs initialization logic + +**📚 Full Documentation:** +For detailed examples, layout replacement, and advanced scenarios: +[Component Replacement Guide](https://abp.io/docs/latest/framework/ui/angular/customization-user-interface) + --- ## 🔒 4. Combined Full-Stack Practices From cecec7505fe5e92efbb2415c4b3774c85bfd0285 Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Thu, 30 Oct 2025 14:36:29 +0300 Subject: [PATCH 5/7] Trim targetProject input before lookup Ensures that leading and trailing whitespace is removed from the targetProject option before attempting to retrieve the project from the workspace. This prevents errors when users accidentally include extra spaces in the project name. --- .../packages/schematics/src/commands/ai-config/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts index 5b510af111..9f1606c885 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts @@ -38,10 +38,11 @@ export default function (options: AiConfigSchema): Rule { let targetPath = '/'; if (options.targetProject) { - const project = workspace.projects.get(options.targetProject); + const trimmedTargetProject = options.targetProject.trim(); + const project = workspace.projects.get(trimmedTargetProject); if (!project) { throw new SchematicsException( - `Project "${options.targetProject}" not found in workspace.` + `Project "${trimmedTargetProject}" not found in workspace.` ); } targetPath = normalize(project.root); From 12fa8bcae98a1ebd019462ae309a28278f4cd83d Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Thu, 20 Nov 2025 16:22:17 +0300 Subject: [PATCH 6/7] Simplify tool string splitting in ai-config command Refactored the splitting of the 'tool' option to use a single regex for whitespace and commas, removing the need for an explicit trim(). This streamlines parsing and improves code clarity. --- .../packages/schematics/src/commands/ai-config/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts index 9f1606c885..1256927a86 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts @@ -17,8 +17,7 @@ export default function (options: AiConfigSchema): Rule { } const tools = options.tool - .split(/[,\s]+/) - .map(t => t.trim()) + .split(/[\s,]+/) .filter(t => t) as AiTool[]; const validTools: AiTool[] = ['claude', 'copilot', 'cursor', 'gemini', 'junie', 'windsurf']; From 2f61a755fb34092a219808906e3897bde2eb5cdf Mon Sep 17 00:00:00 2001 From: sumeyye Date: Mon, 24 Nov 2025 14:22:43 +0300 Subject: [PATCH 7/7] update: ai-tool usage examples --- .../src/commands/ai-config/index.ts | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts index 1256927a86..2cc2da1165 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts @@ -1,4 +1,14 @@ -import { Rule, SchematicsException, Tree, apply, url, mergeWith, MergeStrategy, filter, chain } from '@angular-devkit/schematics'; +import { + Rule, + SchematicsException, + Tree, + apply, + url, + mergeWith, + MergeStrategy, + filter, + chain, +} from '@angular-devkit/schematics'; import { join, normalize } from '@angular-devkit/core'; import { AiConfigSchema, AiTool } from './model'; import { getWorkspace } from '../../utils'; @@ -10,21 +20,21 @@ export default function (options: AiConfigSchema): Rule { console.log(''); console.log('💡 Usage examples:'); console.log(' ng g @abp/ng.schematics:ai-config --tool=claude,cursor'); + console.log(' ng g @abp/ng.schematics:ai-config --tool="claude, cursor"'); + console.log(' ng g @abp/ng.schematics:ai-config --tool=gemini --tool=cursor'); console.log(' ng g @abp/ng.schematics:ai-config --tool=gemini --target-project=my-app'); console.log(''); console.log('Available tools: claude, copilot, cursor, gemini, junie, windsurf'); return tree; } - const tools = options.tool - .split(/[\s,]+/) - .filter(t => t) as AiTool[]; + const tools = options.tool.split(/[\s,]+/).filter(t => t) as AiTool[]; const validTools: AiTool[] = ['claude', 'copilot', 'cursor', 'gemini', 'junie', 'windsurf']; const invalidTools = tools.filter(tool => !validTools.includes(tool)); if (invalidTools.length > 0) { throw new SchematicsException( - `Invalid AI tool(s): ${invalidTools.join(', ')}. Valid options are: ${validTools.join(', ')}` + `Invalid AI tool(s): ${invalidTools.join(', ')}. Valid options are: ${validTools.join(', ')}`, ); } @@ -40,9 +50,7 @@ export default function (options: AiConfigSchema): Rule { const trimmedTargetProject = options.targetProject.trim(); const project = workspace.projects.get(trimmedTargetProject); if (!project) { - throw new SchematicsException( - `Project "${trimmedTargetProject}" not found in workspace.` - ); + throw new SchematicsException(`Project "${trimmedTargetProject}" not found in workspace.`); } targetPath = normalize(project.root); } @@ -51,24 +59,25 @@ export default function (options: AiConfigSchema): Rule { console.log(`📁 Target path: ${targetPath}`); console.log(`🤖 Selected tools: ${tools.join(', ')}`); - const rules: Rule[] = tools - .map(tool => generateConfigForTool(tool, targetPath, options.overwrite || false)); + const rules: Rule[] = tools.map(tool => + generateConfigForTool(tool, targetPath, options.overwrite || false), + ); return chain([ ...rules, (tree: Tree) => { console.log('✅ AI configuration files generated successfully!'); console.log('\n📝 Generated files:'); - + tools.forEach(tool => { const configPath = getConfigPath(tool, targetPath); console.log(` - ${configPath}`); }); console.log('\n💡 Tip: Restart your IDE or AI tool to apply the new configurations.'); - + return tree; - } + }, ]); }; } @@ -76,7 +85,7 @@ export default function (options: AiConfigSchema): Rule { function generateConfigForTool(tool: AiTool, targetPath: string, overwrite: boolean): Rule { return (tree: Tree) => { const configPath = getConfigPath(tool, targetPath); - + if (tree.exists(configPath) && !overwrite) { console.log(`⚠️ Configuration file already exists: ${configPath}`); console.log(` Use --overwrite flag to replace existing files.`); @@ -87,7 +96,7 @@ function generateConfigForTool(tool: AiTool, targetPath: string, overwrite: bool const source = apply(url(sourceDir), [ filter(path => { return !path.endsWith('.DS_Store'); - }) + }), ]); return mergeWith(source, overwrite ? MergeStrategy.Overwrite : MergeStrategy.Default); @@ -101,7 +110,7 @@ function getConfigPath(tool: AiTool, basePath: string): string { cursor: '.cursor/rules/cursor.mdc', gemini: '.gemini/GEMINI.md', junie: '.junie/guidelines.md', - windsurf: '.windsurf/rules/guidelines.md' + windsurf: '.windsurf/rules/guidelines.md', }; const configFile = configFiles[tool];