Skip to content

Commit fbaa1e4

Browse files
committed
Add shorthand-attribute rule
1 parent b37cc88 commit fbaa1e4

32 files changed

+317
-27
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ These rules relate to security vulnerabilities in Svelte code:
254254
| Rule ID | Description | |
255255
|:--------|:------------|:---|
256256
| [@ota-meshi/svelte/no-at-html-tags](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-at-html-tags.html) | disallow use of `{@html}` to prevent XSS attack | :star: |
257-
| [@ota-meshi/svelte/no-target-blank](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-target-blank.html) | disallow target="_blank" attribute without rel="noopener noreferrer" | |
257+
| [@ota-meshi/svelte/no-target-blank](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-target-blank.html) | disallow `target="_blank"` attribute without `rel="noopener noreferrer"` | |
258258

259259
## Best Practices
260260

@@ -276,6 +276,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
276276
| [@ota-meshi/svelte/indent](https://ota-meshi.github.io/eslint-plugin-svelte/rules/indent.html) | enforce consistent indentation | :wrench: |
277277
| [@ota-meshi/svelte/max-attributes-per-line](https://ota-meshi.github.io/eslint-plugin-svelte/rules/max-attributes-per-line.html) | enforce the maximum number of attributes per line | :wrench: |
278278
| [@ota-meshi/svelte/prefer-class-directive](https://ota-meshi.github.io/eslint-plugin-svelte/rules/prefer-class-directive.html) | require class directives instead of ternary expressions | :wrench: |
279+
| [@ota-meshi/svelte/shorthand-attribute](https://ota-meshi.github.io/eslint-plugin-svelte/rules/shorthand-attribute.html) | enforce use of shorthand syntax in attribute | :wrench: |
279280
| [@ota-meshi/svelte/spaced-html-comment](https://ota-meshi.github.io/eslint-plugin-svelte/rules/spaced-html-comment.html) | enforce consistent spacing after the `<!--` and before the `-->` in a HTML comment | :wrench: |
280281

281282
## System

docs/rules/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ These rules relate to security vulnerabilities in Svelte code:
2424
| Rule ID | Description | |
2525
|:--------|:------------|:---|
2626
| [@ota-meshi/svelte/no-at-html-tags](./no-at-html-tags.md) | disallow use of `{@html}` to prevent XSS attack | :star: |
27-
| [@ota-meshi/svelte/no-target-blank](./no-target-blank.md) | disallow target="_blank" attribute without rel="noopener noreferrer" | |
27+
| [@ota-meshi/svelte/no-target-blank](./no-target-blank.md) | disallow `target="_blank"` attribute without `rel="noopener noreferrer"` | |
2828

2929
## Best Practices
3030

@@ -46,6 +46,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
4646
| [@ota-meshi/svelte/indent](./indent.md) | enforce consistent indentation | :wrench: |
4747
| [@ota-meshi/svelte/max-attributes-per-line](./max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: |
4848
| [@ota-meshi/svelte/prefer-class-directive](./prefer-class-directive.md) | require class directives instead of ternary expressions | :wrench: |
49+
| [@ota-meshi/svelte/shorthand-attribute](./shorthand-attribute.md) | enforce use of shorthand syntax in attribute | :wrench: |
4950
| [@ota-meshi/svelte/spaced-html-comment](./spaced-html-comment.md) | enforce consistent spacing after the `<!--` and before the `-->` in a HTML comment | :wrench: |
5051

5152
## System

docs/rules/button-has-type.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This rule aims to warn if no type or an invalid type is used on a button type at
1818

1919
<!--eslint-skip-->
2020

21-
```html
21+
```svelte
2222
<script>
2323
/* eslint @ota-meshi/svelte/button-has-type: "error" */
2424
</script>

docs/rules/comment-directive.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ This rule sends all `eslint-disable`-like comments to the post-process of the `.
3434

3535
<!--eslint-skip-->
3636

37-
```html
37+
```svelte
3838
<script>
3939
/* eslint @ota-meshi/svelte/comment-directive: "error", no-undef: "error" */
4040
</script>
@@ -51,7 +51,7 @@ The `eslint-disable`-like comments can include descriptions to explain why the c
5151

5252
<!--eslint-skip-->
5353

54-
```html
54+
```svelte
5555
<script>
5656
/* eslint @ota-meshi/svelte/comment-directive: "error", no-undef: "error" */
5757
</script>

docs/rules/html-quotes.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ This rule enforces the quotes style of HTML attributes.
2424

2525
<eslint-code-block fix>
2626

27+
<!-- prettier-ignore-start -->
2728
<!--eslint-skip-->
28-
<!-- prettier-ignore -->
29-
```html
29+
30+
```svelte
3031
<script>
3132
/* eslint @ota-meshi/svelte/html-quotes: "error" */
3233
</script>
@@ -40,6 +41,8 @@ This rule enforces the quotes style of HTML attributes.
4041
<img src="{src}" alt='{name} dances.' />
4142
```
4243

44+
<!-- prettier-ignore-end -->
45+
4346
</eslint-code-block>
4447

4548
## :wrench: Options
@@ -59,7 +62,7 @@ This rule enforces the quotes style of HTML attributes.
5962
}
6063
```
6164

62-
- `prefer` ... If `"double"`, requires double quotes. If `"single"` requires single quotes.
65+
- `prefer` ... If `"double"`, requires double quotes. If `"single"`, requires single quotes.
6366
- `dynamic` ... Settings for dynamic attribute values and directive values using curly braces.
6467
- `quoted` ... If `true`, enforce the use of quotes. If `false`, do not allow the use of quotes. The default is `false`.
6568
- `avoidInvalidUnquotedInHTML` ... If `true`, enforces the use of quotes if they are invalid as HTML attribute when not using quotes. The default is `false`.

docs/rules/indent.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ This rule enforces a consistent indentation style in `.svelte`. The default styl
2121

2222
<eslint-code-block fix>
2323

24+
<!-- prettier-ignore-start -->
2425
<!--eslint-skip-->
25-
<!-- prettier-ignore -->
2626

27-
```html
27+
```svelte
2828
<script>
2929
/* eslint @ota-meshi/svelte/indent: "error" */
3030
function click() {}
@@ -33,7 +33,7 @@ This rule enforces a consistent indentation style in `.svelte`. The default styl
3333
<!-- ✓ GOOD -->
3434
<button
3535
type="button"
36-
on:click="{click}"
36+
on:click={click}
3737
class="my-button primally"
3838
>
3939
CLICK ME!
@@ -42,13 +42,15 @@ This rule enforces a consistent indentation style in `.svelte`. The default styl
4242
<!-- ✗ BAD -->
4343
<button
4444
type="button"
45-
on:click="{click}"
45+
on:click={click}
4646
class="my-button primally"
4747
>
4848
CLICK ME!
4949
</button>
5050
```
5151

52+
<!-- prettier-ignore-end -->
53+
5254
</eslint-code-block>
5355

5456
::: warning Note

docs/rules/max-attributes-per-line.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,23 @@ There is a configurable number of attributes that are acceptable in one-line cas
2626

2727
<!--eslint-skip-->
2828

29-
```html
29+
```svelte
3030
<script>
3131
/* eslint @ota-meshi/svelte/max-attributes-per-line: "error" */
3232
</script>
3333
3434
<!-- ✓ GOOD -->
3535
<input
3636
type="text"
37-
bind:value="{text}"
37+
bind:value={text}
3838
{maxlength}
3939
{...attrs}
4040
readonly
4141
size="20"
4242
/>
4343
<button
4444
type="button"
45-
on:click="{click}"
45+
on:click={click}
4646
{maxlength}
4747
{...attrs}
4848
disabled
@@ -52,8 +52,8 @@ There is a configurable number of attributes that are acceptable in one-line cas
5252
</button>
5353
5454
<!-- ✗ BAD -->
55-
<input type="text" bind:value="{text}" {maxlength} {...attrs} readonly />
56-
<button type="button" on:click="{click}" {maxlength} {...attrs}>
55+
<input type="text" bind:value={text} {maxlength} {...attrs} readonly />
56+
<button type="button" on:click={click} {maxlength} {...attrs}>
5757
CLICK ME!
5858
</button>
5959
```

docs/rules/no-inner-declarations.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ This rule supports [svelte-eslint-parser]'s AST.
2525

2626
<!--eslint-skip-->
2727

28-
```html
28+
```svelte
2929
<script>
3030
/* eslint @ota-meshi/svelte/no-inner-declarations: "error" */
3131

docs/rules/no-target-blank.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
pageClass: "rule-details"
33
sidebarDepth: 0
44
title: "@ota-meshi/svelte/no-target-blank"
5-
description: "disallow target=\"_blank\" attribute without rel=\"noopener noreferrer\""
5+
description: "disallow `target=\"_blank\"` attribute without `rel=\"noopener noreferrer\"`"
66
since: "v0.0.4"
77
---
88

99
# @ota-meshi/svelte/no-target-blank
1010

11-
> disallow target="_blank" attribute without rel="noopener noreferrer"
11+
> disallow `target="_blank"` attribute without `rel="noopener noreferrer"`
1212
1313
## :book: Rule Details
1414

@@ -60,7 +60,7 @@ This rule disallows using `target="_blank"` attribute without `rel="noopener nor
6060
/* eslint @ota-meshi/svelte/no-target-blank: ['error', { allowReferrer: false }] */
6161
</script>
6262
63-
<!-- ✓ Good -->
63+
<!-- ✓ GOOD -->
6464
<a href="http://example.com" target="_blank" rel="noopener noreferrer">link</a>
6565
6666
<!-- ✗ BAD -->
@@ -80,7 +80,7 @@ This rule disallows using `target="_blank"` attribute without `rel="noopener nor
8080
/* eslint @ota-meshi/svelte/no-target-blank: ['error', { allowReferrer: true }] */
8181
</script>
8282
83-
<!-- ✓ Good -->
83+
<!-- ✓ GOOD -->
8484
<a href="http://example.com" target="_blank" rel="noopener">link</a>
8585
8686
<!-- ✗ BAD -->
@@ -100,7 +100,7 @@ This rule disallows using `target="_blank"` attribute without `rel="noopener nor
100100
/* eslint @ota-meshi/svelte/no-target-blank: ['error', { enforceDynamicLinks: 'always' }] */
101101
</script>
102102
103-
<!-- ✓ Good -->
103+
<!-- ✓ GOOD -->
104104
<a href={link} target="_blank" rel="noopener noreferrer">link</a>
105105
106106
<!-- ✗ BAD -->
@@ -120,7 +120,7 @@ This rule disallows using `target="_blank"` attribute without `rel="noopener nor
120120
/* eslint @ota-meshi/svelte/no-target-blank: ['error', { enforceDynamicLinks: 'never' }] */
121121
</script>
122122
123-
<!-- ✓ Good -->
123+
<!-- ✓ GOOD -->
124124
<a href={link} target="_blank">link</a>
125125
126126
<!-- ✗ BAD -->

docs/rules/shorthand-attribute.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "@ota-meshi/svelte/shorthand-attribute"
5+
description: "enforce use of shorthand syntax in attribute"
6+
---
7+
8+
# @ota-meshi/svelte/shorthand-attribute
9+
10+
> enforce use of shorthand syntax in attribute
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
13+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
14+
15+
## :book: Rule Details
16+
17+
This rule enforces the use of the shorthand syntax in attribute.
18+
19+
<eslint-code-block fix>
20+
21+
<!-- prettier-ignore-start -->
22+
<!--eslint-skip-->
23+
24+
```svelte
25+
<script>
26+
/* eslint @ota-meshi/svelte/shorthand-attribute: "error" */
27+
</script>
28+
29+
<!-- ✓ GOOD -->
30+
<button {disabled}>...</button>
31+
32+
<!-- ✗ BAD -->
33+
<button disabled={disabled}>...</button>
34+
```
35+
36+
<!-- prettier-ignore-end -->
37+
38+
</eslint-code-block>
39+
40+
## :wrench: Options
41+
42+
```json
43+
{
44+
"@ota-meshi/svelte/shorthand-attribute": [
45+
"error",
46+
{
47+
"prefer": "always" // "never"
48+
}
49+
]
50+
}
51+
```
52+
53+
- `prefer`
54+
- `"always"` ... Expects that the shorthand will be used whenever possible. This is default.
55+
- `"never"` ... Ensures that no shorthand is used in any attribute.
56+
57+
## :mag: Implementation
58+
59+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/shorthand-attribute.ts)
60+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/shorthand-attribute.ts)

docs/rules/spaced-html-comment.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This rule will enforce consistency of spacing after the start of a comment `<!--
2020

2121
<!--eslint-skip-->
2222

23-
```html
23+
```svelte
2424
<script>
2525
/* eslint @ota-meshi/svelte/spaced-html-comment: "error" */
2626
</script>

src/rules/no-target-blank.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ function hasDynamicLink(node: AST.SvelteAttribute["parent"]) {
6363
export default createRule("no-target-blank", {
6464
meta: {
6565
docs: {
66-
description: `disallow target="_blank" attribute without rel="noopener noreferrer"`,
66+
description:
67+
'disallow `target="_blank"` attribute without `rel="noopener noreferrer"`',
6768
category: "Security Vulnerability",
6869
recommended: false,
6970
},

src/rules/shorthand-attribute.ts

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { createRule } from "../utils"
2+
3+
export default createRule("shorthand-attribute", {
4+
meta: {
5+
docs: {
6+
description: "enforce use of shorthand syntax in attribute",
7+
category: "Stylistic Issues",
8+
recommended: false,
9+
},
10+
fixable: "code",
11+
schema: [
12+
{
13+
type: "object",
14+
properties: {
15+
prefer: { enum: ["always", "never"] },
16+
},
17+
additionalProperties: false,
18+
},
19+
],
20+
messages: {
21+
expectedShorthand: "Expected shorthand attribute.",
22+
expectedRegular: "Expected regular attribute syntax.",
23+
},
24+
type: "layout", // "problem", or "layout",
25+
},
26+
create(context) {
27+
const sourceCode = context.getSourceCode()
28+
const always: boolean = context.options[0]?.prefer !== "never"
29+
30+
return always
31+
? {
32+
SvelteAttribute(node) {
33+
if (node.value.length !== 1) {
34+
return
35+
}
36+
const value = node.value[0]
37+
if (
38+
value.type !== "SvelteMustacheTag" ||
39+
value.expression.type !== "Identifier"
40+
) {
41+
return
42+
}
43+
if (node.key.name === value.expression.name) {
44+
context.report({
45+
node,
46+
messageId: "expectedShorthand",
47+
*fix(fixer) {
48+
yield fixer.remove(node.key)
49+
yield fixer.remove(sourceCode.getTokenAfter(node.key)!)
50+
},
51+
})
52+
}
53+
},
54+
}
55+
: {
56+
SvelteShorthandAttribute(node) {
57+
context.report({
58+
node,
59+
messageId: "expectedRegular",
60+
fix(fixer) {
61+
return fixer.insertTextBefore(node, `${node.key.name}=`)
62+
},
63+
})
64+
},
65+
}
66+
},
67+
})

src/utils/rules.ts

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import noInnerDeclarations from "../rules/no-inner-declarations"
1111
import noTargetBlank from "../rules/no-target-blank"
1212
import noUselessMustaches from "../rules/no-useless-mustaches"
1313
import preferClassDirective from "../rules/prefer-class-directive"
14+
import shorthandAttribute from "../rules/shorthand-attribute"
1415
import spacedHtmlComment from "../rules/spaced-html-comment"
1516
import system from "../rules/system"
1617

@@ -27,6 +28,7 @@ export const rules = [
2728
noTargetBlank,
2829
noUselessMustaches,
2930
preferClassDirective,
31+
shorthandAttribute,
3032
spacedHtmlComment,
3133
system,
3234
] as RuleModule[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"options": [{ "prefer": "always" }]
3+
}

0 commit comments

Comments
 (0)