Skip to content

Commit 1399bb9

Browse files
authored
Merge branch 'master' into fix/604
2 parents 4cd9233 + 3aa7f82 commit 1399bb9

File tree

4 files changed

+80
-65
lines changed

4 files changed

+80
-65
lines changed

packages/eslint-plugin-template/docs/rules/no-call-expression.md

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,39 @@ The rule does not have any configuration options.
3535
❌ - Examples of **incorrect** code for this rule:
3636

3737
```html
38-
<div>{{ getInfo() }}</div>
39-
~~~~~~~~~
38+
<div>{{ getInfo()() }}</div>
39+
~~~~~~~~~~~
4040
```
4141

4242
```html
43-
<a href="http://example.com">{{ getInfo().name }}</a>
44-
~~~~~~~~~
43+
<a href="{{ getUrls().user }}"></a>
44+
~~~~~~~~~
4545
```
4646

4747
```html
48-
<a [href]="getUrl()">info</a>
49-
~~~~~~~~
48+
<p [test]="test?.getInfo()"></p>
49+
~~~~~~~~~~~~~~~
5050
```
5151

5252
```html
53-
<a [href]="id && createUrl()">info</a>
54-
~~~~~~~~~~~
53+
<a [href]="id && createUrl() && test()($any)">info</a>
54+
~~~~~~~~~~~ ~~~~~~~~~~~~
5555
{{ id || obj?.nested1() }}
5656
~~~~~~~~~~~~~~
5757
```
5858

5959
```html
60-
<a [href]="id ? a?.createUrl() : editUrl()">info</a>
61-
~~~~~~~~~~~~~~ ~~~~~~~~~
62-
{{ 1 === 2 ? 3 : obj?.nested1() }}
63-
~~~~~~~~~~~~~~
60+
<a [href]="id ? a?.createUrl() : editUrl(3)">info</a>
61+
~~~~~~~~~~~~~~ ~~~~~~~~~~
62+
{{ 1 === 2 ? 3 : obj?.nested1()() }}
63+
~~~~~~~~~~~~~~~~
6464
```
6565

6666
```html
6767
{{ obj?.nested1() }} {{ obj!.nested1() }}
6868
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
69-
<button [type]="obj!.$any(b)!.getType()">info</button>
70-
~~~~~~~~~~~~~~~~~~~~~~~
69+
<button [type]="obj!.$any(b)!.getType()()">info</button>
70+
~~~~~~~~~~~~~~~~~~~~~~~~~
7171
<a [href]="obj.propertyA?.href()">info</a>
7272
~~~~~~~~~~~~~~~~~~~~~
7373
```
@@ -82,10 +82,28 @@ The rule does not have any configuration options.
8282

8383
```html
8484
{{ info }}
85+
```
86+
87+
```html
8588
<button type="button" (click)="handleClick()">Click Here</button>
89+
```
90+
91+
```html
8692
{{ $any(info) }}
93+
```
94+
95+
```html
8796
<input (change)="obj?.changeHandler()">
97+
```
98+
99+
```html
88100
<form [formGroup]="form" (ngSubmit)="form.valid || save()"></form>
101+
```
102+
103+
```html
89104
<form [formGroup]="form" (ngSubmit)="form.valid && save()"></form>
105+
```
106+
107+
```html
90108
<form [formGroup]="form" (ngSubmit)="id ? save() : edit()"></form>
91109
```

packages/eslint-plugin-template/src/rules/no-call-expression.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { MethodCall, SafeMethodCall } from '@angular/compiler';
1+
import type {
2+
FunctionCall,
3+
MethodCall,
4+
SafeMethodCall,
5+
} from '@angular/compiler';
26
import { TmplAstBoundEvent } from '@angular/compiler';
37
import {
48
createESLintRule,
@@ -31,22 +35,24 @@ export default createESLintRule<Options, MessageIds>({
3135
const sourceCode = context.getSourceCode();
3236

3337
return {
34-
'MethodCall[name!="$any"], SafeMethodCall'(
35-
node: MethodCall | SafeMethodCall,
38+
'FunctionCall, MethodCall[name!="$any"], SafeMethodCall'(
39+
node: FunctionCall | MethodCall | SafeMethodCall,
3640
) {
37-
const isChildOfBoundEvent = !!getNearestNodeFrom(node, isBoundEvent);
41+
const isChildOfBoundEvent = Boolean(
42+
getNearestNodeFrom(node, isBoundEvent),
43+
);
3844

3945
if (isChildOfBoundEvent) return;
4046

4147
const {
4248
sourceSpan: { start, end },
4349
} = node;
4450
context.report({
45-
messageId: 'noCallExpression',
4651
loc: {
4752
start: sourceCode.getLocFromIndex(start),
4853
end: sourceCode.getLocFromIndex(end),
4954
},
55+
messageId: 'noCallExpression',
5056
});
5157
},
5258
};

packages/eslint-plugin-template/tests/rules/no-call-expression/cases.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,63 @@ import type { MessageIds } from '../../../src/rules/no-call-expression';
44
const messageId: MessageIds = 'noCallExpression';
55

66
export const valid = [
7-
`
8-
{{ info }}
9-
<button type="button" (click)="handleClick()">Click Here</button>
10-
{{ $any(info) }}
11-
<input (change)="obj?.changeHandler()">
12-
<form [formGroup]="form" (ngSubmit)="form.valid || save()"></form>
13-
<form [formGroup]="form" (ngSubmit)="form.valid && save()"></form>
14-
<form [formGroup]="form" (ngSubmit)="id ? save() : edit()"></form>
15-
`,
7+
'{{ info }}',
8+
'<button type="button" (click)="handleClick()">Click Here</button>',
9+
'{{ $any(info) }}',
10+
'<input (change)="obj?.changeHandler()">',
11+
'<form [formGroup]="form" (ngSubmit)="form.valid || save()"></form>',
12+
'<form [formGroup]="form" (ngSubmit)="form.valid && save()"></form>',
13+
'<form [formGroup]="form" (ngSubmit)="id ? save() : edit()"></form>',
1614
];
1715

1816
export const invalid = [
1917
convertAnnotatedSourceToFailureCase({
20-
description: 'it should fail for call expression in an expression binding',
18+
description: 'should fail for `FunctionCall` within `Interpolation`',
2119
annotatedSource: `
22-
<div>{{ getInfo() }}</div>
23-
~~~~~~~~~
20+
<div>{{ getInfo()() }}</div>
21+
~~~~~~~~~~~
2422
`,
2523
messageId,
2624
}),
2725
convertAnnotatedSourceToFailureCase({
28-
description:
29-
'it should fail when using a property resulted from a call expression in an expression binding',
26+
description: 'should fail for `MethodCall` within `TextAttribute`',
3027
annotatedSource: `
31-
<a href="http://example.com">{{ getInfo().name }}</a>
32-
~~~~~~~~~
28+
<a href="{{ getUrls().user }}"></a>
29+
~~~~~~~~~
3330
`,
3431
messageId,
3532
}),
3633
convertAnnotatedSourceToFailureCase({
37-
description: 'it should fail for call expression in a property binding',
34+
description: 'should fail for `SafeMethodCall` within `BoundAttribute`',
3835
annotatedSource: `
39-
<a [href]="getUrl()">info</a>
40-
~~~~~~~~
36+
<p [test]="test?.getInfo()"></p>
37+
~~~~~~~~~~~~~~~
4138
`,
4239
messageId,
4340
}),
4441
convertAnnotatedSourceToFailureCase({
45-
description: 'it should fail for call expression with binaries',
42+
description:
43+
'should fail for `FunctionCall`, `MethodCall` and `SafeMethodCall` within `Binary`',
4644
annotatedSource: `
47-
<a [href]="id && createUrl()">info</a>
48-
~~~~~~~~~~~
45+
<a [href]="id && createUrl() && test()($any)">info</a>
46+
~~~~~~~~~~~ ^^^^^^^^^^^^
4947
{{ id || obj?.nested1() }}
50-
^^^^^^^^^^^^^^
48+
##############
5149
`,
5250
messages: [
5351
{ char: '~', messageId },
5452
{ char: '^', messageId },
53+
{ char: '#', messageId },
5554
],
5655
}),
5756
convertAnnotatedSourceToFailureCase({
58-
description: 'it should fail for call expression within conditionals',
57+
description:
58+
'should fail for `FunctionCall`, `MethodCall` and `SafeMethodCall` within `Conditional`',
5959
annotatedSource: `
60-
<a [href]="id ? a?.createUrl() : editUrl()">info</a>
61-
~~~~~~~~~~~~~~ ^^^^^^^^^
62-
{{ 1 === 2 ? 3 : obj?.nested1() }}
63-
##############
60+
<a [href]="id ? a?.createUrl() : editUrl(3)">info</a>
61+
~~~~~~~~~~~~~~ ^^^^^^^^^^
62+
{{ 1 === 2 ? 3 : obj?.nested1()() }}
63+
################
6464
`,
6565
messages: [
6666
{ char: '~', messageId },
@@ -69,12 +69,12 @@ export const invalid = [
6969
],
7070
}),
7171
convertAnnotatedSourceToFailureCase({
72-
description: 'it should fail for safe/unsafe method calls',
72+
description: 'should fail for safe/unsafe calls',
7373
annotatedSource: `
7474
{{ obj?.nested1() }} {{ obj!.nested1() }}
7575
~~~~~~~~~~~~~~ ^^^^^^^^^^^^^^
76-
<button [type]="obj!.$any(b)!.getType()">info</button>
77-
#######################
76+
<button [type]="obj!.$any(b)!.getType()()">info</button>
77+
#########################
7878
<a [href]="obj.propertyA?.href()">info</a>
7979
%%%%%%%%%%%%%%%%%%%%%
8080
`,

packages/utils/src/test-helpers.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,22 @@
11
import type { TSESLint } from '@typescript-eslint/experimental-utils';
22

3-
/**
4-
* FROM CODELYZER
5-
*/
6-
interface SourcePosition {
3+
// #region FROM CODELYZER
4+
type SourcePosition = {
75
readonly character: number;
86
readonly line: number;
9-
}
7+
};
108

11-
/**
12-
* FROM CODELYZER
13-
*/
14-
interface ExpectedFailure {
9+
type ExpectedFailure = {
1510
readonly endPosition?: SourcePosition;
1611
readonly message: string;
1712
readonly startPosition?: SourcePosition;
18-
}
13+
};
1914

20-
/**
21-
* FROM CODELYZER
22-
*/
2315
function escapeRegexp(value: string) {
2416
return value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
2517
}
2618

2719
/**
28-
* FROM CODELYZER
29-
*
3020
* When testing a failure, we also test to see if the linter will report the correct place where
3121
* the source code doesn't match the rule.
3222
*
@@ -116,6 +106,7 @@ export function parseInvalidSource(
116106
source: newSource,
117107
};
118108
}
109+
// #endregion FROM CODELYZER
119110

120111
type BaseErrorOptions = {
121112
readonly description: string;

0 commit comments

Comments
 (0)