Skip to content

feat: add svelte/html-closing-bracket-spacing #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
311785f
feat: add `svelte/html-self-closing` rule
marekvospel Jul 11, 2022
be14399
chore: add .idea to gitignore
marekvospel Jul 11, 2022
b5765cb
chore: increase coverage
marekvospel Jul 11, 2022
a57d224
fix(html-self-closing): ignore if element children is whitespace text…
marekvospel Jul 11, 2022
0f5c58d
fix(html-self-closing): add / remove closing from void elements
marekvospel Jul 11, 2022
d446688
feat: add `svelte/no-spaces-around-equal-signs-in-attribute` rule
marekvospel Jul 12, 2022
9648598
fix(no-spaces-around-equal-signs-in-attribute): throw error even if t…
marekvospel Jul 12, 2022
c0952e3
chore(html-self-closing): naming changes
marekvospel Jul 12, 2022
b51e808
docs: add missing jsdoc content
marekvospel Jul 12, 2022
31667c2
chore: add "any" option to html-self-closing, add rules documentation
marekvospel Jul 13, 2022
bf86395
chore: fix megre conflicts, fix `no-spaces...` docs
marekvospel Jul 13, 2022
f230ff9
fix: since version in `html-self-closing` and `no-spaces-around-equal…
marekvospel Jul 15, 2022
aae3c93
feat: add `svelte/html-closing-bracket-spacing` rule
marekvospel Jul 15, 2022
4ba5ffa
docs: add `html-closing-bracket-spacing` docs
marekvospel Jul 15, 2022
bfb519c
chore: lint
marekvospel Jul 16, 2022
6eebfd7
chore: some requested changes
marekvospel Jul 21, 2022
db17e62
docs: fix incorrect default info
marekvospel Jul 21, 2022
77e36eb
chore: use startTag.selfClosing instead of checking for "/". Actually…
marekvospel Jul 21, 2022
6fd94c3
chore: remove .idea from gitignore, disable resolveJsonModule
marekvospel Jul 21, 2022
eacf05b
chore: move each rule into separate PR
marekvospel Jul 21, 2022
34df3a3
chore: change fixable to whitespace
marekvospel Jul 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| Rule ID | Description | |
|:--------|:------------|:---|
| [svelte/first-attribute-linebreak](https://ota-meshi.github.io/eslint-plugin-svelte/rules/first-attribute-linebreak/) | enforce the location of first attribute | :wrench: |
| [svelte/html-closing-bracket-spacing](https://ota-meshi.github.io/eslint-plugin-svelte/rules/html-closing-bracket-spacing/) | require or disallow a space before tag's closing brackets | :wrench: |
| [svelte/html-quotes](https://ota-meshi.github.io/eslint-plugin-svelte/rules/html-quotes/) | enforce quotes style of HTML attributes | :wrench: |
| [svelte/indent](https://ota-meshi.github.io/eslint-plugin-svelte/rules/indent/) | enforce consistent indentation | :wrench: |
| [svelte/max-attributes-per-line](https://ota-meshi.github.io/eslint-plugin-svelte/rules/max-attributes-per-line/) | enforce the maximum number of attributes per line | :wrench: |
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| Rule ID | Description | |
|:--------|:------------|:---|
| [svelte/first-attribute-linebreak](./rules/first-attribute-linebreak.md) | enforce the location of first attribute | :wrench: |
| [svelte/html-closing-bracket-spacing](./rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: |
| [svelte/html-quotes](./rules/html-quotes.md) | enforce quotes style of HTML attributes | :wrench: |
| [svelte/indent](./rules/indent.md) | enforce consistent indentation | :wrench: |
| [svelte/max-attributes-per-line](./rules/max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: |
Expand Down
77 changes: 77 additions & 0 deletions docs/rules/html-closing-bracket-spacing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "svelte/html-closing-bracket-spacing"
description: "require or disallow a space before tag's closing brackets"
---

# svelte/html-closing-bracket-spacing

> require or disallow a space before tag's closing brackets

- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
- :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.

## :book: Rule Details

You can choose either two styles for spacing before closing bracket

- always: `<div />`
- never: `<div/>`

<ESLintCodeBlock fix>

<!-- prettier-ignore-start -->
<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/html-closing-bracket-spacing: "error" */
</script>

<!-- ✓ GOOD -->
<div />
<p>Hello</p>
<div
>
</div>

<!-- ✗ BAD -->
<div/>
<p >Hello</p >
<div >
</div >
```

<!-- prettier-ignore-end -->

</ESLintCodeBlock>

## :wrench: Options

```json
{
"svelte/html-closing-bracket-spacing": [
"error",
{
"startTag": "never", // or "always" or "ignore"
"endTag": "never", // or "always" or "ignore"
"selfClosingTag": "always" // or "never" or "ignore"
}
]
}
```

- `startTag` (`"never"` by default)... Spacing in start tags
- `endTag` (`"never"` by default)... Spacing in end tags
- `selfClosingTag` (`"always"` by default)... Spacing in self closing tags

Every option can be set to
- "always" (`<div />`)
- "never" (`<div/>`)
- "ignore" (either `<div />` or `<div/>`)

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/html-closing-bracket-spacing.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/html-closing-bracket-spacing.ts)
1 change: 1 addition & 0 deletions src/configs/prettier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export = {
rules: {
// eslint-plugin-svelte rules
"svelte/first-attribute-linebreak": "off",
"svelte/html-closing-bracket-spacing": "off",
"svelte/html-quotes": "off",
"svelte/indent": "off",
"svelte/max-attributes-per-line": "off",
Expand Down
109 changes: 109 additions & 0 deletions src/rules/html-closing-bracket-spacing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { createRule } from "../utils"
import type { AST } from "svelte-eslint-parser"

export default createRule("html-closing-bracket-spacing", {
meta: {
docs: {
description: "require or disallow a space before tag's closing brackets",
category: "Stylistic Issues",
conflictWithPrettier: true,
recommended: false,
},
schema: [
{
type: "object",
properties: {
startTag: {
enum: ["always", "never", "ignore"],
},
endTag: {
enum: ["always", "never", "ignore"],
},
selfClosingTag: {
enum: ["always", "never", "ignore"],
},
},
additionalProperties: false,
},
],
messages: {
expectedSpace: "Expected space before '>', but not found.",
unexpectedSpace: "Expected no space before '>', but found.",
},
fixable: "code",
type: "layout",
},
create(ctx) {
const options = {
startTag: "never",
endTag: "never",
selfClosingTag: "always",
...ctx.options[0],
}
const src = ctx.getSourceCode()

/**
* Returns true if string contains newline characters
*/
function containsNewline(string: string): boolean {
return string.includes("\n")
}

/**
* Report
*/
function report(
node: AST.SvelteStartTag | AST.SvelteEndTag,
shouldHave: boolean,
) {
const tagSrc = src.getText(node)
const match = /(\s*)\/?>$/.exec(tagSrc)

const end = node.range[1]
const start = node.range[1] - match![0].length
const loc = {
start: src.getLocFromIndex(start),
end: src.getLocFromIndex(end),
}

ctx.report({
loc,
messageId: shouldHave ? "expectedSpace" : "unexpectedSpace",
*fix(fixer) {
if (shouldHave) {
yield fixer.insertTextBeforeRange([start, end], " ")
} else {
const spaces = match![1]

yield fixer.removeRange([start, start + spaces.length])
}
},
})
}

return {
"SvelteStartTag, SvelteEndTag"(
node: AST.SvelteStartTag | AST.SvelteEndTag,
) {
const tagType =
node.type === "SvelteEndTag"
? "endTag"
: node.selfClosing
? "selfClosingTag"
: "startTag"

if (options[tagType] === "ignore") return

const tagSrc = src.getText(node)
const match = /(\s*)\/?>$/.exec(tagSrc)
if (containsNewline(match![1])) return

if (options[tagType] === "always" && !match![1]) {
report(node, true)
} else if (options[tagType] === "never" && match![1]) {
report(node, false)
}
},
}
},
})
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { RuleModule } from "../types"
import buttonHasType from "../rules/button-has-type"
import commentDirective from "../rules/comment-directive"
import firstAttributeLinebreak from "../rules/first-attribute-linebreak"
import htmlClosingBracketSpacing from "../rules/html-closing-bracket-spacing"
import htmlQuotes from "../rules/html-quotes"
import indent from "../rules/indent"
import maxAttributesPerLine from "../rules/max-attributes-per-line"
Expand Down Expand Up @@ -32,6 +33,7 @@ export const rules = [
buttonHasType,
commentDirective,
firstAttributeLinebreak,
htmlClosingBracketSpacing,
htmlQuotes,
indent,
maxAttributesPerLine,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"options": [
{
"selfClosingTag": "ignore"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"message": "Expected no space before '>', but found.",
"line": 2,
"column": 3
},
{
"message": "Expected no space before '>', but found.",
"line": 2,
"column": 14
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- prettier-ignore -->
<p >Hello</p >
<!-- prettier-ignore -->
<div/>
<div />
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- prettier-ignore -->
<p>Hello</p>
<!-- prettier-ignore -->
<div/>
<div />
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"options": [
{
"endTag": "ignore"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"message": "Expected no space before '>', but found.",
"line": 2,
"column": 3
},
{
"message": "Expected space before '>', but not found.",
"line": 5,
"column": 5
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- prettier-ignore -->
<p >Hello</p >
<p>Hi</p>
<!-- prettier-ignore -->
<div/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- prettier-ignore -->
<p>Hello</p >
<p>Hi</p>
<!-- prettier-ignore -->
<div />
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"options": [
{
"startTag": "ignore"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"message": "Expected no space before '>', but found.",
"line": 2,
"column": 14
},
{
"message": "Expected space before '>', but not found.",
"line": 5,
"column": 5
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- prettier-ignore -->
<p >Hello</p >
<p>Hi</p>
<!-- prettier-ignore -->
<div/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- prettier-ignore -->
<p >Hello</p>
<p>Hi</p>
<!-- prettier-ignore -->
<div />
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"message": "Expected no space before '>', but found.",
"line": 2,
"column": 3
},
{
"message": "Expected no space before '>', but found.",
"line": 2,
"column": 14
},
{
"message": "Expected space before '>', but not found.",
"line": 4,
"column": 5
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!-- prettier-ignore -->
<p >Hello</p >
<!-- prettier-ignore -->
<div/>
<!-- prettier-ignore -->
<div
>
</div

>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!-- prettier-ignore -->
<p>Hello</p>
<!-- prettier-ignore -->
<div />
<!-- prettier-ignore -->
<div
>
</div

>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<p>Hello</p>
<div />
<!-- prettier-ignore -->
<div
/>
<!-- prettier-ignore -->
<div
>
</div

>
16 changes: 16 additions & 0 deletions tests/src/rules/html-closing-bracket-spacing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { RuleTester } from "eslint"
import rule from "../../../src/rules/html-closing-bracket-spacing"
import { loadTestCases } from "../../utils/utils"

const tester = new RuleTester({
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
},
})

tester.run(
"html-closing-bracket-spacing",
rule as any,
loadTestCases("html-closing-bracket-spacing"),
)