From 3092b4bcf77d40af2a6930afe05b6ee6b66ca8b5 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 26 Feb 2026 17:08:22 +0100 Subject: [PATCH] Deprecated JslibModule (#19186) Deprecates the JslibModule and removes unused pipes and directives. --- .../src/directives/a11y-invalid.directive.ts | 32 ----------- .../src/directives/copy-text.directive.ts | 32 ----------- .../src/directives/fallback-src.directive.ts | 25 -------- .../directives/true-false-value.directive.ts | 57 ------------------- libs/angular/src/jslib.module.ts | 27 +++------ libs/angular/src/pipes/search-ciphers.pipe.ts | 42 -------------- .../src/platform/pipes/fingerprint.pipe.ts | 30 ---------- 7 files changed, 8 insertions(+), 237 deletions(-) delete mode 100644 libs/angular/src/directives/a11y-invalid.directive.ts delete mode 100644 libs/angular/src/directives/copy-text.directive.ts delete mode 100644 libs/angular/src/directives/fallback-src.directive.ts delete mode 100644 libs/angular/src/directives/true-false-value.directive.ts delete mode 100644 libs/angular/src/pipes/search-ciphers.pipe.ts delete mode 100644 libs/angular/src/platform/pipes/fingerprint.pipe.ts diff --git a/libs/angular/src/directives/a11y-invalid.directive.ts b/libs/angular/src/directives/a11y-invalid.directive.ts deleted file mode 100644 index 032c08d5332..00000000000 --- a/libs/angular/src/directives/a11y-invalid.directive.ts +++ /dev/null @@ -1,32 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, ElementRef, OnDestroy, OnInit } from "@angular/core"; -import { NgControl } from "@angular/forms"; -import { Subscription } from "rxjs"; - -@Directive({ - selector: "[appA11yInvalid]", - standalone: false, -}) -export class A11yInvalidDirective implements OnDestroy, OnInit { - private sub: Subscription; - - constructor( - private el: ElementRef, - private formControlDirective: NgControl, - ) {} - - ngOnInit() { - this.sub = this.formControlDirective.control.statusChanges.subscribe((status) => { - if (status === "INVALID") { - this.el.nativeElement.setAttribute("aria-invalid", "true"); - } else if (status === "VALID") { - this.el.nativeElement.setAttribute("aria-invalid", "false"); - } - }); - } - - ngOnDestroy() { - this.sub?.unsubscribe(); - } -} diff --git a/libs/angular/src/directives/copy-text.directive.ts b/libs/angular/src/directives/copy-text.directive.ts deleted file mode 100644 index aefb26ef07e..00000000000 --- a/libs/angular/src/directives/copy-text.directive.ts +++ /dev/null @@ -1,32 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, ElementRef, HostListener, Input } from "@angular/core"; - -import { ClientType } from "@bitwarden/common/enums"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -@Directive({ - selector: "[appCopyText]", - standalone: false, -}) -export class CopyTextDirective { - constructor( - private el: ElementRef, - private platformUtilsService: PlatformUtilsService, - ) {} - - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input("appCopyText") copyText: string; - - @HostListener("copy") onCopy() { - if (window == null) { - return; - } - - const timeout = this.platformUtilsService.getClientType() === ClientType.Desktop ? 100 : 0; - setTimeout(() => { - this.platformUtilsService.copyToClipboard(this.copyText, { window: window }); - }, timeout); - } -} diff --git a/libs/angular/src/directives/fallback-src.directive.ts b/libs/angular/src/directives/fallback-src.directive.ts deleted file mode 100644 index b63dc8671cf..00000000000 --- a/libs/angular/src/directives/fallback-src.directive.ts +++ /dev/null @@ -1,25 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, ElementRef, HostListener, Input } from "@angular/core"; - -@Directive({ - selector: "[appFallbackSrc]", - standalone: false, -}) -export class FallbackSrcDirective { - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input("appFallbackSrc") appFallbackSrc: string; - - /** Only try setting the fallback once. This prevents an infinite loop if the fallback itself is missing. */ - private tryFallback = true; - - constructor(private el: ElementRef) {} - - @HostListener("error") onError() { - if (this.tryFallback) { - this.el.nativeElement.src = this.appFallbackSrc; - this.tryFallback = false; - } - } -} diff --git a/libs/angular/src/directives/true-false-value.directive.ts b/libs/angular/src/directives/true-false-value.directive.ts deleted file mode 100644 index 78c1b4647c6..00000000000 --- a/libs/angular/src/directives/true-false-value.directive.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Directive, ElementRef, forwardRef, HostListener, Input, Renderer2 } from "@angular/core"; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; - -// ref: https://juristr.com/blog/2018/02/ng-true-value-directive/ -@Directive({ - selector: "input[type=checkbox][appTrueFalseValue]", - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => TrueFalseValueDirective), - multi: true, - }, - ], - standalone: false, -}) -export class TrueFalseValueDirective implements ControlValueAccessor { - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() trueValue: boolean | string = true; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() falseValue: boolean | string = false; - - constructor( - private elementRef: ElementRef, - private renderer: Renderer2, - ) {} - - @HostListener("change", ["$event"]) - onHostChange(ev: any) { - this.propagateChange(ev.target.checked ? this.trueValue : this.falseValue); - } - - writeValue(obj: any): void { - if (obj === this.trueValue) { - this.renderer.setProperty(this.elementRef.nativeElement, "checked", true); - } else { - this.renderer.setProperty(this.elementRef.nativeElement, "checked", false); - } - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void { - /* nothing */ - } - - setDisabledState?(isDisabled: boolean): void { - /* nothing */ - } - - private propagateChange = (_: any) => { - /* nothing */ - }; -} diff --git a/libs/angular/src/jslib.module.ts b/libs/angular/src/jslib.module.ts index c3670148d67..663e2f39b52 100644 --- a/libs/angular/src/jslib.module.ts +++ b/libs/angular/src/jslib.module.ts @@ -26,11 +26,8 @@ import { import { TwoFactorIconComponent } from "./auth/components/two-factor-icon.component"; import { NotPremiumDirective } from "./billing/directives/not-premium.directive"; -import { A11yInvalidDirective } from "./directives/a11y-invalid.directive"; import { ApiActionDirective } from "./directives/api-action.directive"; import { BoxRowDirective } from "./directives/box-row.directive"; -import { CopyTextDirective } from "./directives/copy-text.directive"; -import { FallbackSrcDirective } from "./directives/fallback-src.directive"; import { IfFeatureDirective } from "./directives/if-feature.directive"; import { InputStripSpacesDirective } from "./directives/input-strip-spaces.directive"; import { InputVerbatimDirective } from "./directives/input-verbatim.directive"; @@ -38,18 +35,23 @@ import { LaunchClickDirective } from "./directives/launch-click.directive"; import { StopClickDirective } from "./directives/stop-click.directive"; import { StopPropDirective } from "./directives/stop-prop.directive"; import { TextDragDirective } from "./directives/text-drag.directive"; -import { TrueFalseValueDirective } from "./directives/true-false-value.directive"; import { CreditCardNumberPipe } from "./pipes/credit-card-number.pipe"; import { PluralizePipe } from "./pipes/pluralize.pipe"; -import { SearchCiphersPipe } from "./pipes/search-ciphers.pipe"; import { SearchPipe } from "./pipes/search.pipe"; import { UserNamePipe } from "./pipes/user-name.pipe"; import { UserTypePipe } from "./pipes/user-type.pipe"; import { EllipsisPipe } from "./platform/pipes/ellipsis.pipe"; -import { FingerprintPipe } from "./platform/pipes/fingerprint.pipe"; import { I18nPipe } from "./platform/pipes/i18n.pipe"; import { IconComponent } from "./vault/components/icon.component"; +/** + * @deprecated In 95% of cases you want I18nPipe from `@bitwarden/ui-common`. In the other 5% + * directly import the relevant directive/pipe/component. If you need one of the non standalone + * pipes/directives/components, make it standalone and import directly. + * + * This module is overly large and adds many unrelated modules to your dependency tree. + * https://angular.dev/guide/ngmodules/overview recommends not using `NgModule`s for new code. + */ @NgModule({ imports: [ ToastModule.forRoot({ @@ -82,57 +84,45 @@ import { IconComponent } from "./vault/components/icon.component"; AutofocusDirective, ], declarations: [ - A11yInvalidDirective, ApiActionDirective, BoxRowDirective, - CopyTextDirective, CreditCardNumberPipe, EllipsisPipe, - FallbackSrcDirective, I18nPipe, IconComponent, InputStripSpacesDirective, InputVerbatimDirective, NotPremiumDirective, - SearchCiphersPipe, SearchPipe, StopClickDirective, StopPropDirective, - TrueFalseValueDirective, LaunchClickDirective, UserNamePipe, UserTypePipe, IfFeatureDirective, - FingerprintPipe, TwoFactorIconComponent, ], exports: [ - A11yInvalidDirective, A11yTitleDirective, ApiActionDirective, AutofocusDirective, ToastModule, BoxRowDirective, - CopyTextDirective, CreditCardNumberPipe, EllipsisPipe, - FallbackSrcDirective, I18nPipe, IconComponent, InputStripSpacesDirective, InputVerbatimDirective, NotPremiumDirective, - SearchCiphersPipe, SearchPipe, StopClickDirective, StopPropDirective, - TrueFalseValueDirective, CopyClickDirective, LaunchClickDirective, UserNamePipe, UserTypePipe, IfFeatureDirective, - FingerprintPipe, TwoFactorIconComponent, TextDragDirective, ], @@ -143,7 +133,6 @@ import { IconComponent } from "./vault/components/icon.component"; SearchPipe, UserNamePipe, UserTypePipe, - FingerprintPipe, PluralizePipe, ], }) diff --git a/libs/angular/src/pipes/search-ciphers.pipe.ts b/libs/angular/src/pipes/search-ciphers.pipe.ts deleted file mode 100644 index cbb595280af..00000000000 --- a/libs/angular/src/pipes/search-ciphers.pipe.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core"; - -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; - -@Pipe({ - name: "searchCiphers", - standalone: false, -}) -export class SearchCiphersPipe implements PipeTransform { - transform(ciphers: CipherView[], searchText: string, deleted = false): CipherView[] { - if (ciphers == null || ciphers.length === 0) { - return []; - } - - if (searchText == null || searchText.length < 2) { - return ciphers.filter((c) => { - return deleted !== c.isDeleted; - }); - } - - searchText = searchText.trim().toLowerCase(); - return ciphers.filter((c) => { - if (deleted !== c.isDeleted) { - return false; - } - if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) { - return true; - } - if (searchText.length >= 8 && c.id.startsWith(searchText)) { - return true; - } - if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(searchText) > -1) { - return true; - } - if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(searchText) > -1) { - return true; - } - - return false; - }); - } -} diff --git a/libs/angular/src/platform/pipes/fingerprint.pipe.ts b/libs/angular/src/platform/pipes/fingerprint.pipe.ts deleted file mode 100644 index 90289ee212b..00000000000 --- a/libs/angular/src/platform/pipes/fingerprint.pipe.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Pipe } from "@angular/core"; - -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { KeyService } from "@bitwarden/key-management"; - -@Pipe({ - name: "fingerprint", - standalone: false, -}) -export class FingerprintPipe { - constructor(private keyService: KeyService) {} - - async transform(publicKey: string | Uint8Array, fingerprintMaterial: string): Promise { - try { - if (typeof publicKey === "string") { - publicKey = Utils.fromB64ToArray(publicKey); - } - - const fingerprint = await this.keyService.getFingerprint(fingerprintMaterial, publicKey); - - if (fingerprint != null) { - return fingerprint.join("-"); - } - - return ""; - } catch { - return ""; - } - } -}