fix-angular-fixmes

bởi bitwarden

Giải quyết các comment ức chế eslint-disable trong toàn bộ codebase của Bitwarden clients bằng cách sửa vấn đề cơ bản. Sử dụng khi người dùng yêu cầu "fix FIXMEs",…

npx skills add https://github.com/bitwarden/clients --skill fix-angular-fixmes

Key rules

  • Fix the underlying issue — never just delete the suppression comment and leave broken code.
  • Remove the complete comment block: FIXME line (if any) + TODO: Skipped block (if any) + eslint-disable-next-line line.
  • Both FIXME-paired and standalone suppressions are the same migration debt.
  • For Angular migration rules, prefer CLI schematics over manual edits.
  • Do NOT convert service observables to signals (ADR-0027).
  • For OnPush and signals patterns, the angular-modernization skill is the authoritative source — this skill only owns the ESLint suppression mechanics.

Step 1: Discover all suppressions

Use the Grep tool to find suppressions in the target path:

  • Pattern eslint-disable — finds all eslint suppressions
  • Pattern FIXME.*CL- — finds Angular FIXME-tracked ones specifically

Group results by rule name. Two forms appear in this codebase:

Form A — FIXME-paired (a FIXME tracking comment sits above the disable):

// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection

Form B — Standalone (disable without a FIXME, or with a CLI skip comment):

// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
// TODO: Skipped for signal migration because:
//  Accessor inputs cannot be migrated as they are too complex.
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals

Both forms must be fixed the same way.

Rule reference

CategoryRuleSection below
Angular@angular-eslint/prefer-on-push-component-change-detectionOnPush
Angular@angular-eslint/prefer-signalsSignals
Angular@angular-eslint/prefer-output-emitter-refSignals
Angular template@angular-eslint/template/button-has-typeHTML rules
TypeScript@typescript-eslint/no-floating-promisesno-floating-promises
TypeScript@typescript-eslint/no-unused-varsno-unused-vars
TypeScript@typescript-eslint/no-unsafe-function-typeno-unsafe-function-type
RxJSrxjs/no-async-subscriberxjs rules
RxJSrxjs-angular/prefer-takeuntilrxjs rules
Bitwarden@bitwarden/platform/no-enumsno-enums
Bitwarden@bitwarden/components/no-bwi-class-usageHTML rules
Generalno-restricted-importsno-restricted-imports
Generalno-consoleno-console
Generalno-emptyno-empty
Generalbare // eslint-disable-next-linebare disable
Tailwindtailwindcss/no-custom-classnameHTML rules

OnPush

Rule: @angular-eslint/prefer-on-push-component-change-detection

Follow the OnPush guidance in the angular-modernization skill (add changeDetection: ChangeDetectionStrategy.OnPush, remove ChangeDetectorRef if only used for detectChanges()). Then remove the FIXME + eslint-disable-next-line lines.

@Directive does not support changeDetection — skip OnPush for pure directives.

Signals

Rules: @angular-eslint/prefer-signals, @angular-eslint/prefer-output-emitter-ref

Applies to @Input(), @Output(), @ViewChild, @ContentChild.

Follow the Signal Inputs, Outputs, and Queries guidance in the angular-modernization skill (prefer CLI schematics, then manual conversion). After each migration, manually remove the FIXME and eslint-disable-next-line lines, and any // TODO: Skipped for signal migration because: comment blocks.

Do NOT convert service observables to signals — only component-local state and decorator bindings (ADR-0027).

no-floating-promises

Rule: @typescript-eslint/no-floating-promises

A returned Promise is not handled. Pick one fix:

// 1. Await it (preferred in async functions)
await this.router.navigate(["/login"]);

// 2. void — explicit fire-and-forget
void this.router.navigate(["/login"]);

// 3. Chain .catch() for explicit error handling
this.router.navigate(["/login"]).catch((err) => this.logService.error(err));

Use void for navigation or toast calls that genuinely don't need awaiting. Use await when the result matters or you're already in an async context.

no-unused-vars

Rule: @typescript-eslint/no-unused-vars

// Remove unused variable
const unused = computeSomething(); // delete this line

// Or prefix with _ if it must be declared (e.g. destructuring)
const [_first, second] = array;

// Or suppress a catch variable (TypeScript 4.0+)
try { ... } catch { ... } // omit the variable entirely

no-unsafe-function-type

Rule: @typescript-eslint/no-unsafe-function-type

Replace the generic Function type with a specific signature:

Before

private callback: Function;

After — use the actual signature

private callback: () => void;
// or for unknown signatures:
private callback: (...args: unknown[]) => unknown;

RxJS rules

Rules: rxjs/no-async-subscribe, rxjs-angular/prefer-takeuntil

rxjs/no-async-subscribe — async callback inside .subscribe() swallows errors:

Before

this.service.data$.subscribe(async (value) => {
  await this.process(value);
});

After — move async work into the pipe

this.service.data$
  .pipe(
    switchMap((value) => this.process(value)),
    takeUntilDestroyed(),
  )
  .subscribe();

rxjs-angular/prefer-takeuntil — subscription without cleanup:

Before

this.service.data$.subscribe((value) => {
  this.data = value;
});

After — add takeUntilDestroyed() (call in constructor or use destroyRef)

constructor() {
  this.service.data$
    .pipe(takeUntilDestroyed())
    .subscribe((value) => { this.data = value; });
}

no-enums

Rule: @bitwarden/platform/no-enums

Convert TypeScript enums to const objects with type aliases (ADR-0025):

Before

enum CipherType {
  Login = 1,
  SecureNote = 2,
}

After

export const CipherType = Object.freeze({ Login: 1, SecureNote: 2 } as const);
export type CipherType = (typeof CipherType)[keyof typeof CipherType];

Update all import sites — the usage (CipherType.Login) stays the same.

no-restricted-imports

Rule: no-restricted-imports

The import is from a path that the ESLint config forbids. Steps:

  1. Read the context around the import to understand what is being imported.
  2. Check eslint.config.mjs at the repo root (or the nearest config) for the no-restricted-imports rule to find the allowed alternative path.
  3. Replace the import with the allowed path and remove the suppression.

Common cases: importing platform-internal modules directly instead of through the public API, or test-only helpers in non-test files.

no-console

Rule: no-console

// Remove debug statements
console.log("debug"); // delete

// Replace with the application logging service
this.logService.error("Something failed", error);

In test files (*.spec.ts), a console.error or console.warn spy may be intentional — in that case, set up the spy properly rather than suppressing:

jest.spyOn(console, "error").mockImplementation(() => {});

no-empty

Rule: no-empty

Empty catch blocks silently swallow errors:

Before

try {
  await something();
  // eslint-disable-next-line no-empty
} catch {}

After — handle or log the error

try {
  await something();
} catch (e) {
  // Intentionally ignored — operation is best-effort
}

// Or log it
try {
  await something();
} catch (e) {
  this.logService.warning("Operation failed", e);
}

Bare disable

Rule: bare // eslint-disable-next-line (no rule specified)

This disables ALL rules for the next line, which is always wrong. Steps:

  1. Remove the suppression and run npm run lint:fix to see which specific rule triggers.
  2. Fix the underlying issue using the appropriate section above.
  3. If the violation truly cannot be fixed (rare), replace the bare disable with a specific named rule.

HTML rules

@angular-eslint/template/button-has-type — Add an explicit type to every <button>:

Before

<button (click)="save()">Save</button>

After

<button type="button" (click)="save()">Save</button>
<!-- or type="submit" inside a <form> -->

@bitwarden/components/no-bwi-class-usage — Replace raw bwi-* icon classes with the <bit-icon> component or the appropriate icon token.

tailwindcss/no-custom-classname — Use a Tailwind utility class with the tw- prefix, or register the class in the Tailwind safelist. Never use arbitrary custom class names.

Step 2: Cleanup checklist per fixed instance

  • // FIXME(…) line removed (if present)
  • // TODO: Skipped for signal migration because: … block removed (all lines, if present)
  • // eslint-disable-next-line … line removed
  • Unused imports removed; new imports added as needed
  • All in-class usages updated (e.g. signal reads need ())

Step 3: Validate

npm run lint:fix

Fix any errors that remain. Run npm run test if behaviour-critical code was changed.

Thêm skills từ bitwarden

figma-to-angular
bitwarden
Kỹ năng này chuyển đổi bản thiết kế Figma thành một component Angular được triển khai đầy đủ với các story Storybook trong monorepo Bitwarden Clients. Đầu ra phải khớp với thiết kế về mặt hình ảnh đồng thời tuân thủ tất cả các quy ước codebase.
force-multiplier
bitwarden
Áp dụng một ý định trên nhiều mục tiêu cùng lúc — một loạt kho lưu trữ trong hệ sinh thái Bitwarden, hoặc nhiều dự án trong một monorepo — như N nhất quán,…
analyzing-git-sessions
bitwarden
Phân tích các commit git và thay đổi trong một khung thời gian hoặc phạm vi commit, cung cấp các bản tóm tắt có cấu trúc cho việc xem xét mã, hồi tưởng, nhật ký công việc hoặc phiên làm việc…
coordinating-cross-team-breakdown
bitwarden
Phối hợp đánh giá và phê duyệt giữa các nhóm cho một Bản Phân Tích Kỹ Thuật Bitwarden. Sử dụng khi xác định các nhóm bị ảnh hưởng, xây dựng bảng phê duyệt Phần 3, theo dõi…
assessing-jira-issue-relevance
bitwarden
Sử dụng khi người dùng cung cấp một mã số vấn đề Jira duy nhất và hỏi liệu vấn đề đó có còn liên quan, còn áp dụng, còn đang chờ xử lý, còn là lỗi, đã được sửa, hay có thể…
assessing-test-coverage
bitwarden
Sử dụng khi xác định mức độ bao phủ kiểm thử ĐÃ tồn tại cho một thay đổi cụ thể (một PR, khóa Jira, tài liệu Tech Breakdown, CSV Testmo, các đường dẫn đã thay đổi, hoặc các mục được đặt tên…
retrospecting
bitwarden
Thực hiện phân tích toàn diện các phiên Claude Code, xem xét lịch sử git, nhật ký hội thoại, thay đổi mã nguồn và thu thập phản hồi từ người dùng để tạo ra…
reviewing-incremental-changes
bitwarden
Sử dụng kỹ năng này khi xem lại một PR đã có bình luận hoặc khi phản hồi các thay đổi của nhà phát triển sau lần xem xét ban đầu. Áp dụng khi có các luồng thảo luận trong PR hoặc…