@yoo-digital/eslint-plugin-angular 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -8,28 +8,106 @@ Here should live all custom Angular lint rules that eslint does not already prov
|
|
|
8
8
|
|
|
9
9
|
Wrong code is yellow underlined in VScode, it can also be raises running : `npm run lint`
|
|
10
10
|
|
|
11
|
-
## Rule 1 : boolean input conversion
|
|
11
|
+
## Rule 1 : boolean input conversion (prefer-boolean-attribute-shorthand)
|
|
12
12
|
|
|
13
13
|
`booleanAttribute @angular/core`
|
|
14
14
|
|
|
15
15
|
`BooleanInput @angular/cdk/coercion`
|
|
16
16
|
|
|
17
|
+
### ⚠️ Important Limitations
|
|
18
|
+
|
|
19
|
+
**Current Implementation:** This rule enforces shorthand syntax (`<x a />` instead of `<x [a]="true" />`) but **cannot automatically verify**:
|
|
20
|
+
- Whether an input has `transform: booleanAttribute`
|
|
21
|
+
- What the default value of an input is
|
|
22
|
+
|
|
23
|
+
This is a technical limitation of Angular ESLint - template rules cannot access the component's TypeScript code.
|
|
24
|
+
|
|
25
|
+
### Recommended Usage
|
|
26
|
+
|
|
27
|
+
This rule works best when:
|
|
28
|
+
1. **All boolean inputs** in your project use `booleanAttribute` transform
|
|
29
|
+
2. **Most boolean inputs** have `default = false` or no default
|
|
30
|
+
3. You treat this as a **style guide enforcer** rather than a safety checker
|
|
31
|
+
|
|
32
|
+
### Configuration
|
|
33
|
+
|
|
34
|
+
By default, the rule only flags `[attr]="true"` bindings (safe default):
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"rules": {
|
|
39
|
+
"@yoo-digital/eslint-plugin-angular/prefer-boolean-attribute-shorthand": "error"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
To also enforce removal of `[attr]="false"` bindings:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"rules": {
|
|
49
|
+
"@yoo-digital/eslint-plugin-angular/prefer-boolean-attribute-shorthand": [
|
|
50
|
+
"error",
|
|
51
|
+
{ "allowFalseLiteral": false }
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
17
57
|
### Examples
|
|
18
58
|
|
|
19
|
-
|
|
59
|
+
#### ✅ Recommended Pattern (Default false or no default)
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Component
|
|
63
|
+
@Input({ transform: booleanAttribute }) disabled: boolean = false;
|
|
64
|
+
// OR
|
|
65
|
+
disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Template:**
|
|
69
|
+
- ❌ `<button [disabled]="true">` → Should be `<button disabled>`
|
|
70
|
+
- ✅ `<button disabled>` (shorthand for true)
|
|
71
|
+
- ✅ `<button>` (omit for false)
|
|
72
|
+
- ✅ `<button [disabled]="isLoading">` (expressions are allowed)
|
|
73
|
+
|
|
74
|
+
#### ⚠️ Special Case: Default true (Disable rule if needed)
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Component
|
|
78
|
+
@Input({ transform: booleanAttribute }) enabled: boolean = true;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Template:**
|
|
82
|
+
```html
|
|
83
|
+
<!-- eslint-disable-next-line @yoo-digital/eslint-plugin-angular/prefer-boolean-attribute-shorthand -->
|
|
84
|
+
<button [enabled]="false">Explicitly disabled</button>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
When default is `true`, you may need explicit `[attr]="true"` or `[attr]="false"` bindings.
|
|
88
|
+
|
|
89
|
+
#### ❌ Without booleanAttribute (Won't work!)
|
|
20
90
|
|
|
21
|
-
|
|
91
|
+
```typescript
|
|
92
|
+
// Component - Missing transform!
|
|
93
|
+
@Input() checked: boolean = false;
|
|
94
|
+
```
|
|
22
95
|
|
|
23
|
-
|
|
96
|
+
**Template:**
|
|
97
|
+
- ❌ `<input checked>` → Will pass `""` (empty string), NOT boolean!
|
|
98
|
+
- ✅ `<input [checked]="true">` (must use property binding)
|
|
24
99
|
|
|
25
|
-
### Decorator
|
|
100
|
+
### Decorator Syntax
|
|
26
101
|
|
|
27
|
-
`@Input({ transform: booleanAttribute }) myInput: boolean =
|
|
102
|
+
`@Input({ transform: booleanAttribute }) myInput: boolean = false;`
|
|
28
103
|
|
|
29
|
-
### Signal
|
|
104
|
+
### Signal Input Syntax
|
|
30
105
|
|
|
31
|
-
`myInput = input<boolean, BooleanInput>(
|
|
106
|
+
`myInput = input<boolean, BooleanInput>(false, { transform: booleanAttribute });`
|
|
32
107
|
|
|
33
|
-
###
|
|
108
|
+
### Summary
|
|
34
109
|
|
|
35
|
-
|
|
110
|
+
- **Rule enforces**: `[attr]="true"` → `attr` shorthand
|
|
111
|
+
- **Rule assumes**: Inputs have `booleanAttribute` and `default ≠ true`
|
|
112
|
+
- **Manual override**: Use eslint-disable comments for special cases
|
|
113
|
+
- **Best practice**: Ensure all boolean inputs use `booleanAttribute`
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
allowFalseLiteral?: boolean;
|
|
4
|
-
}
|
|
5
|
-
type MessageIds = 'preferTrue' | 'preferFalse' | 'suggestTrue' | 'suggestRemove';
|
|
2
|
+
type MessageIds = 'preferTrue' | 'suggestTrue';
|
|
6
3
|
export declare const RULE_NAME = "prefer-boolean-attribute-shorthand";
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* This rule enforces shorthand syntax for boolean inputs bound to true.
|
|
6
|
+
*
|
|
7
|
+
* BEHAVIOR:
|
|
8
|
+
* - [attr]="true" → Warns and suggests: attr
|
|
9
|
+
* - [attr]="false" → No warning (ignored)
|
|
10
|
+
* - attr → OK (already shorthand)
|
|
11
|
+
*
|
|
12
|
+
* ASSUMPTIONS:
|
|
13
|
+
* This rule assumes that boolean inputs use Angular's booleanAttribute transform,
|
|
14
|
+
* which allows the shorthand syntax to work correctly. The presence of the attribute
|
|
15
|
+
* alone (without a binding) will be interpreted as true.
|
|
16
|
+
*
|
|
17
|
+
* LIMITATIONS:
|
|
18
|
+
* Cannot verify at lint-time whether:
|
|
19
|
+
* - The input actually has `transform: booleanAttribute`
|
|
20
|
+
* - The input's default value
|
|
21
|
+
*
|
|
22
|
+
* Use this rule in projects where boolean inputs consistently use booleanAttribute.
|
|
23
|
+
*/
|
|
24
|
+
export declare const preferBooleanAttributeShorthandRule: TSESLint.RuleModule<MessageIds, []>;
|
|
8
25
|
export {};
|
|
@@ -3,39 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.preferBooleanAttributeShorthandRule = exports.RULE_NAME = void 0;
|
|
4
4
|
const utils_1 = require("@angular-eslint/utils");
|
|
5
5
|
exports.RULE_NAME = 'prefer-boolean-attribute-shorthand';
|
|
6
|
+
/**
|
|
7
|
+
* This rule enforces shorthand syntax for boolean inputs bound to true.
|
|
8
|
+
*
|
|
9
|
+
* BEHAVIOR:
|
|
10
|
+
* - [attr]="true" → Warns and suggests: attr
|
|
11
|
+
* - [attr]="false" → No warning (ignored)
|
|
12
|
+
* - attr → OK (already shorthand)
|
|
13
|
+
*
|
|
14
|
+
* ASSUMPTIONS:
|
|
15
|
+
* This rule assumes that boolean inputs use Angular's booleanAttribute transform,
|
|
16
|
+
* which allows the shorthand syntax to work correctly. The presence of the attribute
|
|
17
|
+
* alone (without a binding) will be interpreted as true.
|
|
18
|
+
*
|
|
19
|
+
* LIMITATIONS:
|
|
20
|
+
* Cannot verify at lint-time whether:
|
|
21
|
+
* - The input actually has `transform: booleanAttribute`
|
|
22
|
+
* - The input's default value
|
|
23
|
+
*
|
|
24
|
+
* Use this rule in projects where boolean inputs consistently use booleanAttribute.
|
|
25
|
+
*/
|
|
6
26
|
exports.preferBooleanAttributeShorthandRule = {
|
|
7
27
|
meta: {
|
|
8
28
|
type: 'suggestion',
|
|
9
29
|
docs: {
|
|
10
|
-
description: 'Prefer boolean input attribute shorthand
|
|
30
|
+
description: 'Prefer boolean input attribute shorthand when binding to true (e.g., use "disabled" instead of [disabled]="true").',
|
|
11
31
|
},
|
|
12
32
|
hasSuggestions: true,
|
|
13
|
-
schema: [
|
|
14
|
-
{
|
|
15
|
-
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
allowFalseLiteral: { type: 'boolean' },
|
|
18
|
-
},
|
|
19
|
-
additionalProperties: false,
|
|
20
|
-
},
|
|
21
|
-
],
|
|
33
|
+
schema: [],
|
|
22
34
|
messages: {
|
|
23
35
|
preferTrue: 'Use attribute shorthand "{{attr}}" instead of [{{attr}}]="true".',
|
|
24
|
-
preferFalse: 'Avoid binding [{{attr}}]="false"; remove the binding or ensure default is false.',
|
|
25
36
|
suggestTrue: 'Replace with attribute shorthand {{attr}}',
|
|
26
|
-
suggestRemove: 'Remove the false binding',
|
|
27
37
|
},
|
|
28
38
|
},
|
|
29
|
-
defaultOptions: [
|
|
30
|
-
{
|
|
31
|
-
allowFalseLiteral: false,
|
|
32
|
-
},
|
|
33
|
-
],
|
|
39
|
+
defaultOptions: [],
|
|
34
40
|
create(context) {
|
|
35
|
-
// Robust to missing options: default to [{}] if undefined
|
|
36
|
-
const optionsArr = context.options ?? [{}];
|
|
37
|
-
const options = optionsArr[0] ?? {};
|
|
38
|
-
const allowFalseLiteral = options.allowFalseLiteral ?? false;
|
|
39
41
|
const parserServices = (0, utils_1.getTemplateParserServices)(context);
|
|
40
42
|
return {
|
|
41
43
|
BoundAttribute(node) {
|
|
@@ -43,12 +45,14 @@ exports.preferBooleanAttributeShorthandRule = {
|
|
|
43
45
|
if (!value || !value.ast)
|
|
44
46
|
return;
|
|
45
47
|
const ast = value.ast;
|
|
48
|
+
// Only check for boolean literals
|
|
46
49
|
if (ast?.constructor?.name === 'LiteralPrimitive' && typeof ast.value === 'boolean') {
|
|
47
|
-
|
|
48
|
-
const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
|
|
49
|
-
const start = node.sourceSpan.start.offset;
|
|
50
|
-
const end = node.sourceSpan.end.offset;
|
|
50
|
+
// Only warn for [attr]="true", ignore [attr]="false"
|
|
51
51
|
if (ast.value === true) {
|
|
52
|
+
const attrName = node.name;
|
|
53
|
+
const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
|
|
54
|
+
const start = node.sourceSpan.start.offset;
|
|
55
|
+
const end = node.sourceSpan.end.offset;
|
|
52
56
|
context.report({
|
|
53
57
|
loc,
|
|
54
58
|
messageId: 'preferTrue',
|
|
@@ -62,20 +66,7 @@ exports.preferBooleanAttributeShorthandRule = {
|
|
|
62
66
|
],
|
|
63
67
|
});
|
|
64
68
|
}
|
|
65
|
-
|
|
66
|
-
context.report({
|
|
67
|
-
loc,
|
|
68
|
-
messageId: 'preferFalse',
|
|
69
|
-
data: { attr: attrName },
|
|
70
|
-
suggest: [
|
|
71
|
-
{
|
|
72
|
-
messageId: 'suggestRemove',
|
|
73
|
-
data: { attr: attrName },
|
|
74
|
-
fix: (fixer) => fixer.replaceTextRange([start, end], ''),
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
});
|
|
78
|
-
}
|
|
69
|
+
// [attr]="false" is explicitly ignored - no warning
|
|
79
70
|
}
|
|
80
71
|
},
|
|
81
72
|
};
|
package/package.json
CHANGED