--- name: migration-lint description: Comprehensive Angular migration compliance checker with auto-fix capabilities. Use when reviewing migrated code for DDD architecture, Angular 20 syntax, and UI pattern compliance. --- # Migration Lint Command Review and fix migrated Angular code for compliance. This command reads ALL migration guidelines and performs a comprehensive checklist review. ## Arguments - `$ARGUMENTS` - Target path to review (e.g., `libs/mxsecurity/connection-management-page`) ## Workflow ### Step 1: Load ALL Migration Guidelines **IMPORTANT:** Read the core SKILL.md first, then reference documents as needed from ``. **Core:** `one-ui-migration skill` | # | Topic | File | Category | |---|-------|------|----------| | 1 | Migration Context | rules/reference/migration-context.md | Foundation | | 2 | Angular 20 Syntax | rules/reference/angular-20-syntax.md | Foundation | | 3 | DDD Architecture | rules/reference/ddd-architecture.md | Architecture | | 4 | State Management | rules/reference/state-management.md | Architecture | | 5 | Authentication | rules/tools/auth.md | Architecture | | 6 | Migration Checklist | rules/reference/migration-checklist.md | Foundation | | 7 | Pitfalls Index | rules/reference/pitfalls/index.md | Pitfalls | | 7a | Pitfalls: Translation & Layout | rules/reference/pitfalls/translation-layout.md | Pitfalls | | 7b | Pitfalls: DDD Violations | rules/reference/pitfalls/ddd-violations.md | Pitfalls | | 7c | Pitfalls: Angular Syntax | rules/reference/pitfalls/angular-syntax.md | Pitfalls | | 7d | Pitfalls: Forms & Services | rules/reference/pitfalls/forms-services.md | Pitfalls | | 8 | Validators | rules/tools/forms/validators.md | Forms | | 9 | Error Handling | rules/tools/forms/error-handling.md | Forms | | 10 | Form Patterns | rules/tools/forms/patterns.md | Forms | | 11 | Page Layout | rules/tools/ui/page-layout.md | UI Patterns | | 12a | Forms | rules/tools/ui/forms.md | UI Patterns | | 12b | Buttons | rules/tools/ui/buttons.md | UI Patterns | | 12c | Components | rules/tools/ui/components.md | UI Patterns | | 13 | Dialogs | rules/tools/ui/dialogs.md | UI Patterns | | 14 | Table Basics | rules/tools/tables/basics.md | Tables | | 15 | Table Columns | rules/tools/tables/columns.md | Tables | | 16 | Table Advanced | rules/tools/tables/advanced.md | Tables | | 17 | API Type Definitions | rules/reference/api-types.md | API | | 18 | Shared Stores | rules/reference/shared-stores.md | State | ### Step 2: Read Target Files Read all `.ts`, `.html`, and `.scss` files in the target path: - `{target}/domain/src/lib/**/*.ts` - `{target}/features/src/lib/**/*.ts` - `{target}/features/src/lib/**/*.html` - `{target}/features/src/lib/**/*.scss` - `{target}/ui/src/lib/**/*.ts` - `{target}/ui/src/lib/**/*.html` - `{target}/ui/src/lib/**/*.scss` - `{target}/shell/src/lib/**/*.ts` ### Step 3: Comprehensive Checklist Review **Check EVERY item from EVERY migration file:** --- ## Checklist by Category ### Category 1: Foundation & Pitfalls #### migration-context.md - [ ] Project follows one-ui monorepo structure - [ ] Uses correct scope tags (`scope:mxsecurity`) - [ ] Library types match DDD pattern (`domain`, `features`, `ui`, `shell`) #### angular-syntax.md - [ ] Uses `@if` instead of `*ngIf` - [ ] Uses `@for (item of items; track item.id)` instead of `*ngFor` - [ ] Uses `@switch` instead of `ngSwitch` - [ ] Uses `inject()` instead of constructor injection - [ ] Uses `input()` instead of `@Input()` - [ ] Uses `output()` instead of `@Output()` - [ ] Uses `viewChild()` / `viewChildren()` instead of `@ViewChild()` / `@ViewChildren()` - [ ] Uses `contentChild()` / `contentChildren()` instead of `@ContentChild()` / `@ContentChildren()` - [ ] Uses `model()` for two-way binding where appropriate - [ ] Components are standalone (default in Angular 20) - [ ] Uses `ChangeDetectionStrategy.OnPush` #### migration-checklist.md - [ ] Domain layer contains only business logic, models, services, state - [ ] Features layer contains smart components with store injection - [ ] UI layer contains presentational components with input/output ONLY (NO store injection) - [ ] Shell layer contains routing configuration - [ ] Barrel exports (`index.ts`) are properly configured #### pitfalls/translation-layout.md - [ ] Translation keys match source exactly (no new keys created) - [ ] Form layout matches source (row groupings preserved) - [ ] No padding in page component styles - [ ] Page uses `gl-page-content` class (not ng-container) - [ ] Table toolbar button order: Refresh → Create/Delete (only if old code has refresh) #### pitfalls/ddd-violations.md - [ ] No form templates in features layer (extract to UI) - [ ] No store injection in UI components - [ ] Dialogs are in features layer (not UI) - [ ] No business logic in features (move to domain) - [ ] No HTTP calls in UI components #### pitfalls/angular-syntax.md - [ ] No mixing old/new control flow (`*ngIf` with `@for`) - [ ] All `@for` loops have `track` - [ ] No NgModule patterns (use standalone) - [ ] `langChanged()` called at field level (not in computed) - [ ] Uses `inject()` instead of constructor injection #### pitfalls/forms-services.md - [ ] `mutationMethod`/`queryMethod` imported from `@one-ui/mxsecurity/shared/domain` (NOT `shared/util`) - [ ] No unnecessary `.def.ts` files for constants - [ ] Uses `MxSnackbarService` (not `MatSnackBar`) - [ ] Uses `controls.xxx` (not `.get('xxx')`) - [ ] Form valueChanges uses `toSignal()` in effects - [ ] Uses `EMPTY_DASH` constant (not `utils.emptyDash`) - [ ] Uses `oneUiNumberOnly` (not `appNumberOnly`) - [ ] Uses `mx-password-input` for password fields - [ ] Uses `mx-key-value` for readonly displays - [ ] No `TRANSLOCO_SCOPE` provider (use global translation keys) - [ ] No endpoint constants (`#ENDPOINTS = {}`) - inline URLs directly in API methods --- ### Category 2: Architecture #### ddd-architecture.md - [ ] Domain layer has correct structure - [ ] Features layer has correct structure - [ ] UI layer has correct structure - [ ] Shell layer has correct structure - [ ] Dependencies flow correctly (UI → Domain, Features → UI/Domain) - [ ] No UI components importing from features - [ ] Shared domain utilities used where appropriate #### state-management.md - [ ] Uses NgRx SignalStore for state management - [ ] Store follows correct naming convention (`XxxStore`) - [ ] Uses `withEntities()` for entity management - [ ] Uses `withRequestStatus()` for loading states - [ ] Uses `withDataService()` for API integration - [ ] Effects handle success/error properly - [ ] Uses `patchState()` correctly - [ ] Store is provided in correct scope #### authentication.md - [ ] Uses `AuthStore` for authentication state - [ ] Permission checks use correct patterns - [ ] Protected routes have guards - [ ] API calls include proper authentication headers --- ### Category 3: Forms (forms/) #### validators.md - [ ] Uses `OneValidators.required` (not `Validators.required`) - [ ] Uses `OneValidators.email` (not `Validators.email`) - [ ] Uses `OneValidators.minLength(n)` (not `Validators.minLength`) - [ ] Uses `OneValidators.maxLength(n)` (not `Validators.maxLength`) - [ ] Uses `OneValidators.min(n)` (not `Validators.min`) - [ ] Uses `OneValidators.max(n)` (not `Validators.max`) - [ ] Uses `OneValidators.pattern(x)` (not `Validators.pattern`) - [ ] Custom validators follow correct patterns - [ ] Cross-field validators implemented correctly - [ ] Import from `@one-ui/mxsecurity/shared/domain` #### error-handling.md - [ ] Form errors displayed using `` - [ ] Error messages use translation keys - [ ] Async validators handle errors - [ ] API errors displayed to user - [ ] Uses `MxSnackbarService` for notifications #### patterns.md - [ ] Uses `NonNullableFormBuilder` (not `FormBuilder`) - [ ] Form controls accessed via `controls.xxx` (not `.get('xxx')`) - [ ] `getRawValue()` used for form submission - [ ] Dynamic forms use `FormArray` correctly - [ ] Form reset/patch follows patterns - [ ] Forms in UI layer use `input()`/`output()` for data flow --- ### Category 4: UI Patterns (ui/) #### ui/page-layout.md - [ ] Root wrapper has `class="gl-page-content"` - [ ] Uses `` at top - [ ] Uses `` for page title - [ ] Content wrapped in `class="content-wrapper"` (NOT `mat-card`) - [ ] Uses `*transloco="let t"` pattern - [ ] Section titles use `class="gl-title-md"` #### buttons.md - [ ] Submit button uses `mat-flat-button color="primary"` - [ ] Cancel button uses `mat-button` - [ ] Toolbar buttons use `mat-stroked-button` - [ ] NO `mat-raised-button` (use `mat-flat-button`) - [ ] Loading state uses `[mxButtonIsLoading]` directive - [ ] Disabled state properly handles `noPermission()` #### forms.md - [ ] Single form field NOT wrapped in `
` - [ ] Multiple fields on same row use `
` #### dialogs.md - [ ] Uses shared dialog config (`smallDialogConfig`, `mediumDialogConfig`, `largeDialogConfig`) - [ ] Has `viewContainerRef: this.#viewContainerRef` when dialog uses store - [ ] `ConfirmDialogComponent` result checked as boolean (NOT `result?.confirm`) - [ ] Dialog data typed properly - [ ] Dialog returns correct type - [ ] `afterClosed()` subscription cleaned up properly --- ### Category 5: Tables (tables/) #### basics.md - [ ] Uses `` component - [ ] Column definitions use `MxColumnDef[]` - [ ] Has `data-testid` attributes - [ ] Loading state shows skeleton - [ ] Empty state handled properly #### columns.md - [ ] Column types match data - [ ] Custom templates import `MatTableModule` and `MatSortModule` - [ ] Link columns use proper navigation - [ ] Status columns use proper styling - [ ] Action columns positioned correctly #### advanced.md - [ ] Selection implemented correctly (if applicable) - [ ] Sorting configured properly - [ ] Pagination implemented (if applicable) - [ ] Filtering works correctly (if applicable) - [ ] Toolbar buttons use `mat-stroked-button` --- ### Category 6: API & Services #### api-types.md - [ ] API types defined in domain layer - [ ] Request/Response types properly typed - [ ] Uses proper HTTP methods - [ ] Error responses typed - [ ] Enums defined for constants - [ ] NO endpoint constants (`#ENDPOINTS = {...}`) - inline URL strings directly in methods #### shared-stores.md - [ ] Uses shared stores where appropriate (e.g., `TimeZoneStore`) - [ ] Shared store initialization handled - [ ] Cross-feature state accessed via shared stores - [ ] No duplicate state management --- ### Category 7: Additional Checks #### Translation (Transloco) - [ ] Uses `*transloco="let t"` or `@let t = translocoT` - [ ] NO `translate` pipe - [ ] Translation keys follow naming convention - [ ] Parameters passed correctly to `t()` #### Directives - [ ] `oneUiNumberOnly` (not `appNumberOnly`) - [ ] Custom directives use `one-ui` prefix #### Services - [ ] `MxSnackbarService` (not `MatSnackBar`) - [ ] Snackbar: `snackBar.open(msg)` (not `snackBar.open(msg, '', { duration: 3000 })`) #### Constants - [ ] `EMPTY_DASH` from `@one-ui/mxsecurity/shared/domain` (not `utils.emptyDash`) #### Imports - [ ] No circular imports - [ ] Imports from correct library paths - [ ] No relative imports crossing library boundaries #### Password Fields - [ ] Uses `` for password fields #### Readonly Display - [ ] Uses `` for readonly key-value displays --- ### Step 4: Fix Issues **Auto-fix the following issues:** | Issue | Fix | |-------|-----| | `mat-raised-button` | → `mat-flat-button` | | `*ngIf` | → `@if` | | `*ngFor` | → `@for (item of items; track item.id)` | | `Validators.required` | → `OneValidators.required` | | `Validators.email` | → `OneValidators.email` | | `Validators.minLength(n)` | → `OneValidators.minLength(n)` | | `Validators.maxLength(n)` | → `OneValidators.maxLength(n)` | | `.get('xxx')` | → `controls.xxx` | | `class="section-title"` | → `class="gl-title-md"` | | `appNumberOnly` | → `oneUiNumberOnly` | | `this.utils.emptyDash` | → `EMPTY_DASH` | | `readonly #ENDPOINTS = {` | → Remove constant, inline URLs directly in each method | | `from '@one-ui/mxsecurity/shared/util'` (for mutationMethod/queryMethod) | → `from '@one-ui/mxsecurity/shared/domain'` | ### Step 5: Generate Report Output a compliance report: ```markdown ## Migration Lint Report **Path:** {target_path} **Date:** {current_date} ### Checklist Summary | Category | Files Checked | Pass | Fail | N/A | |----------|---------------|------|------|-----| | Foundation (01, 02, 06) | 3 | Pass | Fail | - | | Architecture (03, 04, 05) | 3 | Pass | Fail | - | | Pitfalls (08, 08a, 08b, 08c, 08d) | 5 | Pass | Fail | - | | Forms (09a, 09b, 09c) | 3 | Pass | Fail | - | | UI Patterns (10a, 10b, 10c) | 3 | Pass | Fail | - | | Tables (11a, 11b, 11c) | 3 | Pass | Fail | - | | API & Services (12, 13) | 2 | Pass | Fail | - | | **Total** | **22** | **X** | **Y** | **Z** | ### Issues Fixed | File | Line | Issue | Fix Applied | |------|------|-------|-------------| | component.html | 15 | mat-raised-button | Changed to mat-flat-button | ### Issues Found (Manual Review Needed) | File | Line | Issue | Recommendation | Guide Ref | |------|------|-------|----------------|-----------| | component.ts | 42 | Missing viewContainerRef | Add viewContainerRef to dialog config | dialogs.md | | store.ts | 20 | Store in UI layer | Move to features layer | ddd-architecture.md | ### Detailed Checklist Results #### migration-context.md (Pass) - [x] Project follows one-ui monorepo structure - [x] Uses correct scope tags #### angular-syntax.md (Warning) - [x] Uses @if instead of *ngIf - [ ] Uses inject() instead of constructor injection (line 45) ... (continue for all files) ### Summary - **Migration Files Checked:** 22/22 - **Auto-fixed:** X issues - **Manual review needed:** Y issues - **Passed checks:** Z items - **Failed checks:** W items ``` ## Quick Reference ### Button Types ```html ``` ### Page Layout ```html
``` ### Form Row ```html {{ t('label') }}
... ...
``` ### Dialog Config ```typescript import { mediumDialogConfig } from '@one-ui/mxsecurity/shared/domain'; this.#dialog.open(MyDialog, { ...mediumDialogConfig, data: dialogData, viewContainerRef: this.#viewContainerRef // Required if dialog uses store }); ``` ### Validators ```typescript import { OneValidators } from '@one-ui/mxsecurity/shared/domain'; this.#fb.group({ name: ['', [OneValidators.required, OneValidators.maxLength(32)]], email: ['', [OneValidators.required, OneValidators.email]], port: ['', [OneValidators.required, OneValidators.min(1), OneValidators.max(65535)]] }); ``` ### ConfirmDialogComponent ```typescript // ConfirmDialogComponent returns boolean, NOT { confirm: boolean } const dialogRef = this.#dialog.open( ConfirmDialogComponent, { ...mediumDialogConfig, viewContainerRef: this.#viewContainerRef, data: { title: '...', desc: '...' } } ); dialogRef.afterClosed().subscribe((result) => { if (result) { // result is boolean this.doSomething(); } }); ``` ### Snackbar Service ```typescript // Use MxSnackbarService instead of MatSnackBar import { MxSnackbarService } from '@moxa/formoxa/mx-snackbar'; readonly #snackBar = inject(MxSnackbarService); // Simple usage this.#snackBar.open('Success message'); // Persistent message (no auto-dismiss) this.#snackBar.open('Saving...', { duration: 0 }); ``` ### Section Title ```html

{{ t('section.title') }}

{{ t('section.title') }}

``` ### Form Controls Access ```typescript // .get() - no type safety const value = this.form.get('username')?.value; // controls.xxx - type safe const value = this.form.controls.username.value; // In ValidatorFn - use type assertion #myValidator(): ValidatorFn { return (formGroup: AbstractControl): null => { const controls = (formGroup as typeof this.form).controls; const fieldValue = controls.fieldName.value; return null; }; } ``` ### Password Input ```html ``` ### Key-Value Display ```html ``` ### UI Component Pattern ```typescript // UI components use input()/output() ONLY - NO store injection export class MyUiComponent { readonly data = input(); readonly isLoading = input(false); readonly save = output(); readonly cancel = output(); } ``` ### Features Component Pattern ```typescript // Features components can inject stores export class MyFeatureComponent { readonly #store = inject(MyStore); readonly data = this.#store.data; readonly isLoading = this.#store.isLoading; } ```