Skip to content

Commit

Permalink
feat(eslint-plugin-template): [no-call-expression] add allowList opti…
Browse files Browse the repository at this point in the history
…on (#1217)
  • Loading branch information
sandikbarr committed Nov 24, 2022
1 parent de2994c commit a69c809
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 6 deletions.
45 changes: 44 additions & 1 deletion packages/eslint-plugin-template/docs/rules/no-call-expression.md
Expand Up @@ -23,7 +23,17 @@ Disallows calling expressions in templates, except for output handlers

## Rule Options

The rule does not have any configuration options.
The rule accepts an options object with the following properties:

```ts
interface Options {
/**
* Default: `[]`
*/
allowList?: string[];
}

```

<br>

Expand Down Expand Up @@ -391,6 +401,39 @@ The rule does not have any configuration options.
<form [formGroup]="form" (ngSubmit)="id ? save() : edit()"></form>
```

<br>

---

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/no-call-expression": [
"error",
{
"allowList": [
"nested",
"getHref"
]
}
]
}
}
```

<br>

#### ✅ Valid Code

```html
{{ obj?.nested() }} {{ obj!.nested() }}
<a [href]="getHref()">info</a>
```

</details>

<br>
46 changes: 41 additions & 5 deletions packages/eslint-plugin-template/src/rules/no-call-expression.ts
@@ -1,10 +1,14 @@
import type { Call } from '@angular-eslint/bundled-angular-compiler';
import type { AST, Call } from '@angular-eslint/bundled-angular-compiler';
import { TmplAstBoundEvent } from '@angular-eslint/bundled-angular-compiler';
import { ensureTemplateParser } from '@angular-eslint/utils';
import { createESLintRule } from '../utils/create-eslint-rule';
import { getNearestNodeFrom } from '../utils/get-nearest-node-from';

type Options = [];
type Options = [
{
readonly allowList?: readonly string[];
},
];
export type MessageIds = 'noCallExpression';
export const RULE_NAME = 'no-call-expression';

Expand All @@ -17,13 +21,25 @@ export default createESLintRule<Options, MessageIds>({
'Disallows calling expressions in templates, except for output handlers',
recommended: false,
},
schema: [],
schema: [
{
additionalProperties: false,
properties: {
allowList: {
items: { type: 'string' },
type: 'array',
uniqueItems: true,
},
},
type: 'object',
},
],
messages: {
noCallExpression: 'Avoid calling expressions in templates',
},
},
defaultOptions: [],
create(context) {
defaultOptions: [{ allowList: [] }],
create(context, [{ allowList }]) {
ensureTemplateParser(context);
const sourceCode = context.getSourceCode();

Expand All @@ -35,6 +51,8 @@ export default createESLintRule<Options, MessageIds>({

if (isChildOfBoundEvent) return;

if (isCallNameInAllowList(node.receiver, allowList)) return;

const {
sourceSpan: { start, end },
} = node;
Expand All @@ -53,3 +71,21 @@ export default createESLintRule<Options, MessageIds>({
function isBoundEvent(node: unknown): node is TmplAstBoundEvent {
return node instanceof TmplAstBoundEvent;
}

function isASTWithName(
ast: AST & { name?: string },
): ast is AST & { name: string } {
return !!ast.name;
}

function isCallNameInAllowList(
ast: AST & { name?: string },
allowList?: readonly string[],
): boolean | undefined {
return (
allowList &&
allowList.length > 0 &&
isASTWithName(ast) &&
allowList.indexOf(ast.name) > -1
);
}
Expand Up @@ -11,6 +11,17 @@ export const valid = [
'<form [formGroup]="form" (ngSubmit)="form.valid || save()"></form>',
'<form [formGroup]="form" (ngSubmit)="form.valid && save()"></form>',
'<form [formGroup]="form" (ngSubmit)="id ? save() : edit()"></form>',
{
code: `
{{ obj?.nested() }} {{ obj!.nested() }}
<a [href]="getHref()">info</a>
`,
options: [
{
allowList: ['nested', 'getHref'],
},
],
},
];

export const invalid = [
Expand Down

0 comments on commit a69c809

Please sign in to comment.