Skip to content

Commit

Permalink
feat(eslint-plugin-template): allow alias option in [use-track-by-f…
Browse files Browse the repository at this point in the history
…unction] (#1497)
  • Loading branch information
arturovt committed Oct 15, 2023
1 parent edfb9f7 commit 354d394
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 19 deletions.
223 changes: 222 additions & 1 deletion packages/eslint-plugin-template/docs/rules/use-track-by-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ Ensures trackBy function is used

## Rule Options

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

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

```

<br>

Expand Down Expand Up @@ -69,6 +79,42 @@ The rule does not have any configuration options.

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/use-track-by-function": [
"error",
{
"alias": [
"ngForTrackByProperty"
]
}
]
}
}
```

<br>

#### ❌ Invalid Code

```html
<ul>
<li *ngFor="let item of [1, 2, 3];">
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{ item }}
</li>
</ul>
```

<br>

---

<br>

#### Default Config

```json
Expand Down Expand Up @@ -98,6 +144,40 @@ The rule does not have any configuration options.

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/use-track-by-function": [
"error",
{
"alias": [
"ngForTrackByProperty"
]
}
]
}
}
```

<br>

#### ❌ Invalid Code

```html
<ng-template ngFor let-item [ngForOf]="[1, 2, 3]" let-i="index">
~~~~~~~~~~~~~~~~~~~~~
{{ item }}
</ng-template>
```

<br>

---

<br>

#### Default Config

```json
Expand Down Expand Up @@ -159,6 +239,48 @@ The rule does not have any configuration options.
</ng-template>
```

<br>

---

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/use-track-by-function": [
"error",
{
"alias": [
"ngForTrackByProperty"
]
}
]
}
}
```

<br>

#### ❌ Invalid Code

```html
<div *ngFor="let item of [1, 2, 3]; trackBy: trackByFn">
{{ item }}
</div>
<div *ngFor="let item of [1, 2, 3]; trackByProperty: trackByFn">
{{ item }}
</div>
<ul>
<li *ngFor="let item of [1, 2, 3];">
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{ item }}
</li>
</ul>
```

</details>

<br>
Expand Down Expand Up @@ -398,6 +520,105 @@ The rule does not have any configuration options.
">
```

<br>

---

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/use-track-by-function": [
"error",
{
"alias": [
"ngForTrackByProperty"
]
}
]
}
}
```

<br>

#### ✅ Valid Code

```html
<div *ngFor="
let item of [1, 2, 3];
let i = index;
trackByProperty: 'id'
">
```

<br>

---

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/use-track-by-function": [
"error",
{
"alias": [
"ngForTrackById"
]
}
]
}
}
```

<br>

#### ✅ Valid Code

```html
<div *ngFor="let photo of photos; trackById"></div>
```

<br>

---

<br>

#### Custom Config

```json
{
"rules": {
"@angular-eslint/template/use-track-by-function": [
"error",
{
"alias": [
"ngForTrackByProperty"
]
}
]
}
}
```

<br>

#### ✅ Valid Code

```html
<ng-template ngFor let-item [ngForOf]="[1, 2, 3]" let-i="index" [ngForTrackByProperty]="trackByFn">
{{ item }}
</ng-template>
```

</details>

<br>
12 changes: 6 additions & 6 deletions packages/eslint-plugin-template/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
export default {
displayName: 'eslint-plugin-template',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
},
],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/packages/eslint-plugin-template',
Expand Down
39 changes: 27 additions & 12 deletions packages/eslint-plugin-template/src/rules/use-track-by-function.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import type {
TmplAstTemplate,
TmplAstTextAttribute,
TmplAstBoundAttribute,
} from '@angular-eslint/bundled-angular-compiler';
import { TmplAstBoundAttribute } from '@angular-eslint/bundled-angular-compiler';
import { getTemplateParserServices } from '@angular-eslint/utils';
import { createESLintRule } from '../utils/create-eslint-rule';

type Options = [];
type Options = [{ readonly alias: readonly string[] }];
export type MessageIds = 'useTrackByFunction';
export const RULE_NAME = 'use-track-by-function';

const DEFAULT_ALIAS = [] as const;

export default createESLintRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
Expand All @@ -18,13 +20,27 @@ export default createESLintRule<Options, MessageIds>({
description: 'Ensures trackBy function is used',
recommended: false,
},
schema: [],
schema: [
{
type: 'object',
properties: {
alias: {
type: 'array',
items: {
type: 'string',
},
},
},
additionalProperties: false,
},
],
messages: {
useTrackByFunction: 'Missing trackBy function in ngFor directive',
},
},
defaultOptions: [],
create(context) {
defaultOptions: [{ alias: DEFAULT_ALIAS }],
create(context, [{ alias }]) {
const isNgForTrackBy = isNgForTrackByFactory(alias);
const parserServices = getTemplateParserServices(context);

return {
Expand Down Expand Up @@ -72,11 +88,10 @@ export default createESLintRule<Options, MessageIds>({
},
});

function isNgForTrackBy(
attribute: TmplAstBoundAttribute | TmplAstTextAttribute,
): attribute is TmplAstBoundAttribute & { name: 'ngForTrackBy' } {
return (
attribute instanceof TmplAstBoundAttribute &&
attribute.name === 'ngForTrackBy'
);
const DEFAULT_NG_FOR_TRACK_BY_ATTRIBUTE_NAME = 'ngForTrackBy';

function isNgForTrackByFactory(alias: readonly string[]) {
const names = [...alias, DEFAULT_NG_FOR_TRACK_BY_ATTRIBUTE_NAME];
return (attribute: TmplAstBoundAttribute | TmplAstTextAttribute) =>
names.includes(attribute.name);
}
Loading

0 comments on commit 354d394

Please sign in to comment.