Skip to content

Commit dbb9df3

Browse files
authored
build: add linting around unsafe takeUntil usage (#19720)
Adds a lint rule to catch unsafe usages of `takeUntil`.
1 parent 56ec32b commit dbb9df3

File tree

6 files changed

+71
-21
lines changed

6 files changed

+71
-21
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"core-js": "^2.6.9",
6060
"material-components-web": "7.0.0-canary.7461aad68.0",
6161
"rxjs": "^6.5.3",
62+
"rxjs-tslint-rules": "^4.33.1",
6263
"systemjs": "0.19.43",
6364
"tslib": "^2.0.0",
6465
"zone.js": "~0.10.3"

src/cdk-experimental/column-resize/column-resize.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ export abstract class ColumnResize implements AfterViewInit, OnDestroy {
9191
this.notifier.triggerResize.pipe(mapTo(undefined)),
9292
this.notifier.resizeCompleted.pipe(mapTo(undefined))
9393
).pipe(
94-
takeUntil(this.destroyed),
95-
take(1),
94+
take(1),
95+
takeUntil(this.destroyed),
9696
).subscribe(() => {
9797
this.setResized();
9898
});

src/cdk-experimental/column-resize/overlay-handle.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {coerceCssPixelValue} from '@angular/cdk/coercion';
1111
import {Directionality} from '@angular/cdk/bidi';
1212
import {ESCAPE} from '@angular/cdk/keycodes';
1313
import {CdkColumnDef} from '@angular/cdk/table';
14-
import {fromEvent, Subject} from 'rxjs';
14+
import {fromEvent, Subject, merge} from 'rxjs';
1515
import {
1616
distinctUntilChanged,
1717
filter,
@@ -59,21 +59,19 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
5959

6060
private _listenForMouseEvents() {
6161
this.ngZone.runOutsideAngular(() => {
62-
const takeUntilDestroyed = takeUntil<MouseEvent>(this.destroyed);
63-
6462
fromEvent<MouseEvent>(this.elementRef.nativeElement!, 'mouseenter').pipe(
65-
takeUntilDestroyed,
6663
mapTo(this.resizeRef.origin.nativeElement!),
64+
takeUntil(this.destroyed),
6765
).subscribe(cell => this.eventDispatcher.headerCellHovered.next(cell));
6866

6967
fromEvent<MouseEvent>(this.elementRef.nativeElement!, 'mouseleave').pipe(
70-
takeUntilDestroyed,
7168
map(event => event.relatedTarget &&
7269
_closest(event.relatedTarget as Element, HEADER_CELL_SELECTOR)),
70+
takeUntil(this.destroyed)
7371
).subscribe(cell => this.eventDispatcher.headerCellHovered.next(cell));
7472

7573
fromEvent<MouseEvent>(this.elementRef.nativeElement!, 'mousedown')
76-
.pipe(takeUntilDestroyed).subscribe(mousedownEvent => {
74+
.pipe(takeUntil(this.destroyed)).subscribe(mousedownEvent => {
7775
this._dragStarted(mousedownEvent);
7876
});
7977
});
@@ -100,11 +98,11 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
10098

10199
this.updateResizeActive(true);
102100

103-
mouseup.pipe(takeUntil(escape), takeUntil(this.destroyed)).subscribe(({screenX}) => {
101+
mouseup.pipe(takeUntil(merge(escape, this.destroyed))).subscribe(({screenX}) => {
104102
this._notifyResizeEnded(size, screenX !== startX);
105103
});
106104

107-
escape.pipe(takeUntil(mouseup), takeUntil(this.destroyed)).subscribe(() => {
105+
escape.pipe(takeUntil(merge(mouseup, this.destroyed))).subscribe(() => {
108106
this._notifyResizeEnded(initialSize);
109107
});
110108

@@ -113,9 +111,7 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
113111
startWith(startX),
114112
distinctUntilChanged(),
115113
pairwise(),
116-
takeUntil(mouseup),
117-
takeUntil(escape),
118-
takeUntil(this.destroyed),
114+
takeUntil(merge(mouseup, escape, this.destroyed))
119115
).subscribe(([prevX, currX]) => {
120116
let deltaX = currX - prevX;
121117

src/cdk-experimental/popover-edit/table-directives.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ export class CdkEditable implements AfterViewInit, OnDestroy {
107107
handler => element.addEventListener('focus', handler, true),
108108
handler => element.removeEventListener('focus', handler, true)
109109
).pipe(
110-
takeUntil(this.destroyed),
111110
toClosest(ROW_SELECTOR),
112111
share(),
112+
takeUntil(this.destroyed),
113113
).subscribe(this.editEventDispatcher.focused);
114114

115115
merge(
@@ -119,22 +119,22 @@ export class CdkEditable implements AfterViewInit, OnDestroy {
119119
),
120120
fromEvent<KeyboardEvent>(element, 'keydown').pipe(filter(event => event.key === 'Escape'))
121121
).pipe(
122-
takeUntil(this.destroyed),
123122
mapTo(null),
124123
share(),
124+
takeUntil(this.destroyed),
125125
).subscribe(this.editEventDispatcher.focused);
126126

127127
// Keep track of rows within the table. This is used to know which rows with hover content
128128
// are first or last in the table. They are kept focusable in case focus enters from above
129129
// or below the table.
130130
this.ngZone.onStable.pipe(
131-
takeUntil(this.destroyed),
132131
// Optimization: ignore dom changes while focus is within the table as we already
133132
// ensure that rows above and below the focused/active row are tabbable.
134133
withLatestFrom(this.editEventDispatcher.editingOrFocused),
135134
filter(([_, activeRow]) => activeRow == null),
136135
map(() => element.querySelectorAll(ROW_SELECTOR)),
137136
share(),
137+
takeUntil(this.destroyed),
138138
).subscribe(this.editEventDispatcher.allRows);
139139

140140
fromEvent<KeyboardEvent>(element, 'keydown').pipe(
@@ -310,10 +310,9 @@ export class CdkPopoverEdit<C> implements AfterViewInit, OnDestroy {
310310
// scroll position and viewport size.
311311
merge(this.services.scrollDispatcher.scrolled(), this.services.viewportRuler.change())
312312
.pipe(
313-
startWith(null),
314-
takeUntil(this.overlayRef!.detachments()),
315-
takeUntil(this.destroyed),
316-
)
313+
startWith(null),
314+
takeUntil(merge(this.overlayRef!.detachments(), this.destroyed))
315+
)
317316
.subscribe(() => {
318317
this._updateOverlaySize();
319318
});

tslint.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2+
"extends": [
3+
"rxjs-tslint-rules"
4+
],
25
"rulesDirectory": [
36
"./tools/tslint-rules/",
47
"node_modules/vrsource-tslint-rules/rules",
@@ -107,6 +110,10 @@
107110
"template-no-negated-async": true,
108111
"use-lifecycle-interface": true,
109112

113+
// RxJS
114+
"rxjs-no-unsafe-takeuntil": true,
115+
"rxjs-no-unsafe-catch": true,
116+
110117
// Custom Rules
111118
"ts-loader": true,
112119
"no-exposed-todo": true,

yarn.lock

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,13 @@
11711171
dependencies:
11721172
"@types/node" ">= 8"
11731173

1174+
"@phenomnomnominal/tsquery@^4.0.0":
1175+
version "4.1.0"
1176+
resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.0.tgz#9c836d6db829b5127ccc1ffd8e4c2ad08d600071"
1177+
integrity sha512-+i1eqUJODVanUDuTdOPgnjErFg21DKGLstdRXp4LLGcSbO7c+3pwJPkmdSfbkh9gO6xaHJ/5ftSAMqEFJF5cGA==
1178+
dependencies:
1179+
esquery "^1.0.1"
1180+
11741181
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
11751182
version "1.1.2"
11761183
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@@ -3859,6 +3866,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
38593866
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
38603867
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
38613868

3869+
decamelize@^4.0.0:
3870+
version "4.0.0"
3871+
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
3872+
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
3873+
38623874
decode-uri-component@^0.2.0:
38633875
version "0.2.0"
38643876
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@@ -4537,6 +4549,13 @@ esprima@^4.0.0, esprima@~4.0.0:
45374549
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
45384550
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
45394551

4552+
esquery@^1.0.1:
4553+
version "1.3.1"
4554+
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57"
4555+
integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==
4556+
dependencies:
4557+
estraverse "^5.1.0"
4558+
45404559
estraverse@^1.9.1:
45414560
version "1.9.3"
45424561
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
@@ -4547,6 +4566,11 @@ estraverse@^4.1.0, estraverse@^4.2.0:
45474566
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
45484567
integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
45494568

4569+
estraverse@^5.1.0:
4570+
version "5.1.0"
4571+
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642"
4572+
integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==
4573+
45504574
estree-walker@^0.6.1:
45514575
version "0.6.1"
45524576
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
@@ -10318,6 +10342,19 @@ [email protected]:
1031810342
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
1031910343
integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=
1032010344

10345+
rxjs-tslint-rules@^4.33.1:
10346+
version "4.33.1"
10347+
resolved "https://registry.yarnpkg.com/rxjs-tslint-rules/-/rxjs-tslint-rules-4.33.1.tgz#c94f9b4ccec4fcbb6697033899dfe1298056aebf"
10348+
integrity sha512-sFhSDmVzqf653/qbAnhDBZ2xMFpr4eek3e5jFNpfweCvRD5/Vb/vf1/c8LMsuj0wvA7ZU2Vl2E53nrAvxIdOig==
10349+
dependencies:
10350+
"@phenomnomnominal/tsquery" "^4.0.0"
10351+
decamelize "^4.0.0"
10352+
resolve "^1.4.0"
10353+
semver "^7.0.0"
10354+
tslib "^2.0.0"
10355+
tsutils "^3.0.0"
10356+
tsutils-etc "^1.2.2"
10357+
1032110358
1032210359
version "6.5.5"
1032310360
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
@@ -10529,6 +10566,11 @@ semver@^6.3.0:
1052910566
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
1053010567
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
1053110568

10569+
semver@^7.0.0:
10570+
version "7.3.2"
10571+
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
10572+
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
10573+
1053210574
semver@~5.0.1:
1053310575
version "5.0.3"
1053410576
resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
@@ -11880,6 +11922,11 @@ tslint@~5.1.0:
1188011922
semver "^5.3.0"
1188111923
tsutils "^1.4.0"
1188211924

11925+
tsutils-etc@^1.2.2:
11926+
version "1.2.2"
11927+
resolved "https://registry.yarnpkg.com/tsutils-etc/-/tsutils-etc-1.2.2.tgz#cdeeb777574a5c1b15b27658cb8424f7f7139831"
11928+
integrity sha512-5g2cXpD1OoVc/MLZxh5PuHXhlnYQmuRiW66e1n91j+2J/Pw5lfmVcZAghoDVBdltDXGaCjy8ZttXaX2u/MjHgg==
11929+
1188311930
1188411931
version "2.27.2"
1188511932
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7"
@@ -11899,7 +11946,7 @@ tsutils@^2.29.0:
1189911946
dependencies:
1190011947
tslib "^1.8.1"
1190111948

11902-
tsutils@^3.17.1:
11949+
tsutils@^3.0.0, tsutils@^3.17.1:
1190311950
version "3.17.1"
1190411951
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
1190511952
integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==

0 commit comments

Comments
 (0)