Skip to content

Commit

Permalink
Merge pull request #136 from rrd108/rrd/if-without-curly-braces
Browse files Browse the repository at this point in the history
feat: add rrd if without curly braces rule w tests fix #111
  • Loading branch information
rrd108 authored Aug 13, 2024
2 parents 4da9fa3 + 7e690d1 commit b124f4f
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default defineConfig({
{ text: 'Else Condition', link: '/rules/rrd/else-condition' },
{ text: 'Function Size', link: '/rules/rrd/function-size' },
{ text: 'HTML links', link: '/rules/rrd/html-link' },
{ text: 'If Without Curly Braces', link: '/rules/rrd/if-without-curly-braces' },
{ text: 'Magic Numbers', link: '/rules/rrd/magic-numbers' },
{ text: 'Parameter Count', link: '/rules/rrd/parameter-count' },
{ text: 'Plain Script', link: '/rules/rrd/plain-script' },
Expand Down
37 changes: 37 additions & 0 deletions docs/rules/rrd/if-without-curly-braces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# If Without Curly Braces

Check if `if` statements are missing curly braces, even when they contain only one line of code.

## 📖 What does this rule enforce?

This rule ensures that all `if` statements, regardless of the number of lines they contain, use curly braces `{}` to enclose the code block.

## ❓ Why it's good to follow this rule?

- **Readability:** Curly braces make the structure of the code clear, reducing the likelihood of misunderstandings. Even if the `if` statement has only one line, using braces ensures that the code is easy to read and follow.
- **Consistency:** Enforcing curly braces for all `if` statements helps maintain a consistent coding style throughout the project, making the codebase easier to navigate.
- **Maintainability:** Code changes often, and what starts as a one-line `if` statement can easily grow into something more complex. If braces are already in place, adding new lines of code becomes less error-prone.
- **Avoiding Bugs:** Omitting braces can lead to subtle bugs, especially when additional statements are added later. With braces, there's no ambiguity about which statements are controlled by the `if`.

## 😱 Examples of code for which this rule will throw a warning

::: warning
The following code omits curly braces in the `if` statement:
:::

```ts
if (condition)
doSomething()
```

## 🤩 How to fix it?

::: tip
Always use curly braces to enclose the code block within `if` statements, even if it's just one line.
:::

```ts
if (condition) {
doSomething()
}
```
1 change: 1 addition & 0 deletions docs/rules/rrd/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ These ruleset is the most opinionated with rules that are not part of the *offic
- [Else Condition](./else-condition.md)
- [Function Size](./function-size.md)
- [HTML links](./html-link.md)
- [If Without Curly Braces](./if-without-curly-braces.md)
- [Magic Numbers](./magic-numbers.md)
- [Parameter Count](./parameter-count.md)
- [Plain Script](./plain-script.md)
Expand Down
86 changes: 86 additions & 0 deletions src/rules/rrd/ifWithoutCurlyBraces.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { beforeEach, describe, expect, it } from 'vitest'
import type { SFCScriptBlock } from '@vue/compiler-sfc'
import { BG_ERR, BG_RESET, TEXT_INFO, TEXT_RESET, TEXT_WARN } from '../asceeCodes'
import { checkIfWithoutCurlyBraces, reportIfWithoutCurlyBraces, resetIfWithoutCurlyBraces } from './ifWithoutCurlyBraces'

describe('ifWithoutCurlyBraces', () => {
beforeEach(() => {
resetIfWithoutCurlyBraces()
})

it('should not report files where if statements use curly braces', () => {
const script = {
content: `
<script setup>
if (isLoading) {
doSomething();
}
</script>
`,
} as SFCScriptBlock
const filename = 'if-with-curly-braces.vue'
checkIfWithoutCurlyBraces(script, filename)
expect(reportIfWithoutCurlyBraces().length).toBe(0)
expect(reportIfWithoutCurlyBraces()).toStrictEqual([])
})

it('should not report files where if statements are written in a single line with curly braces', () => {
const script = {
content: `
<script setup>
if (isLoading) { doSomething(); }
if (isError) { handleError(); }
</script>
`,
} as SFCScriptBlock
const filename = 'if-with-single-line-curly-braces.vue'
checkIfWithoutCurlyBraces(script, filename)
expect(reportIfWithoutCurlyBraces().length).toBe(0)
expect(reportIfWithoutCurlyBraces()).toStrictEqual([])
})

it('should report files where an if statement does not use curly braces', () => {
const script = {
content: `
<script setup>
if (isLoading) doSomething();
</script>
`,
} as SFCScriptBlock
const filename = 'if-without-curly-braces.vue'
const statement = 'if (isLoading) doSomething();'
checkIfWithoutCurlyBraces(script, filename)
expect(reportIfWithoutCurlyBraces().length).toBe(1)
expect(reportIfWithoutCurlyBraces()).toStrictEqual([{
file: filename,
rule: `${TEXT_INFO}rrd ~ if without curly braces${TEXT_RESET}`,
description: `👉 ${TEXT_WARN}All if statements must be enclosed in curly braces for better readability and maintainability.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/if-without-curly-braces.html`,
message: `line #2 if statement without curly braces: ${BG_ERR}${statement}${BG_RESET} 🚨`,
}])
})

it('should report files where multiple if statements do not use curly braces', () => {
const script = {
content: `
<script setup>
if (isLoading) doSomething();
if (isError) handleError();
</script>
`,
} as SFCScriptBlock
const filename = 'multiple-if-without-curly-braces.vue'
checkIfWithoutCurlyBraces(script, filename)
expect(reportIfWithoutCurlyBraces().length).toBe(2)
expect(reportIfWithoutCurlyBraces()).toStrictEqual([{
file: filename,
rule: `${TEXT_INFO}rrd ~ if without curly braces${TEXT_RESET}`,
description: `👉 ${TEXT_WARN}All if statements must be enclosed in curly braces for better readability and maintainability.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/if-without-curly-braces.html`,
message: `line #2 if statement without curly braces: ${BG_ERR}if (isLoading) doSomething();${BG_RESET} 🚨`,
}, {
file: filename,
rule: `${TEXT_INFO}rrd ~ if without curly braces${TEXT_RESET}`,
description: `👉 ${TEXT_WARN}All if statements must be enclosed in curly braces for better readability and maintainability.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/if-without-curly-braces.html`,
message: `line #3 if statement without curly braces: ${BG_ERR}if (isError) handleError();${BG_RESET} 🚨`,
}])
})
})
50 changes: 50 additions & 0 deletions src/rules/rrd/ifWithoutCurlyBraces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { SFCScriptBlock } from '@vue/compiler-sfc'
import { BG_ERR, BG_RESET, TEXT_INFO, TEXT_RESET, TEXT_WARN } from '../asceeCodes'
import type { FileCheckResult, Offense } from '../../types'

const results: FileCheckResult[] = []

const checkIfWithoutCurlyBraces = (script: SFCScriptBlock | null, filePath: string) => {
if (!script) {
return
}

const content = script.content
const lines = content.split('\n')

lines.forEach((line, index) => {
const trimmedLine = line.trim()

// Check if the line contains an `if` statement and is not followed by curly braces
if (trimmedLine.startsWith('if (') && !trimmedLine.includes('{')) {
// Check if the next line is not a continuation of the if statement with curly braces
const nextLine = lines[index + 1]?.trim()
if (!nextLine || (!nextLine.startsWith('{') && !trimmedLine.endsWith('{'))) {
results.push({
filePath,
message: `line #${index} if statement without curly braces: ${BG_ERR}${trimmedLine}${BG_RESET}`,
})
}
}
})
}

const reportIfWithoutCurlyBraces = () => {
const offenses: Offense[] = []

if (results.length > 0) {
results.forEach((result) => {
offenses.push({
file: result.filePath,
rule: `${TEXT_INFO}rrd ~ if without curly braces${TEXT_RESET}`,
description: `👉 ${TEXT_WARN}All if statements must be enclosed in curly braces for better readability and maintainability.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/if-without-curly-braces.html`,
message: `${result.message} 🚨`,
})
})
}
return offenses
}

const resetIfWithoutCurlyBraces = () => (results.length = 0)

export { checkIfWithoutCurlyBraces, reportIfWithoutCurlyBraces, resetIfWithoutCurlyBraces }
1 change: 1 addition & 0 deletions src/rules/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const RULES = {
'elseCondition',
'functionSize',
'htmlLink',
'ifWithoutCurlyBraces',
'magicNumbers',
'parameterCount',
'plainScript',
Expand Down
2 changes: 2 additions & 0 deletions src/rulesCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { checkElementSelectorsWithScoped } from './rules/vue-caution/elementSele
import { checkHtmlLink } from './rules/rrd/htmlLink'
import { checkMagicNumbers } from './rules/rrd/magicNumbers'
import { checkMultiAttributeElements } from './rules/vue-strong/multiAttributeElements'
import { checkIfWithoutCurlyBraces } from './rules/rrd/ifWithoutCurlyBraces'

export const checkRules = (descriptor: SFCDescriptor, filePath: string, apply: Array<RuleSetType>) => {
const script = descriptor.scriptSetup || descriptor.script
Expand Down Expand Up @@ -72,6 +73,7 @@ export const checkRules = (descriptor: SFCDescriptor, filePath: string, apply: A
checkElseCondition(script, filePath)
checkFunctionSize(script, filePath)
checkHtmlLink(descriptor.template, filePath)
checkIfWithoutCurlyBraces(script, filePath)
checkMagicNumbers(script, filePath)
checkParameterCount(script, filePath)
checkPlainScript(descriptor.script, filePath)
Expand Down
2 changes: 2 additions & 0 deletions src/rulesReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { reportHtmlLink } from './rules/rrd/htmlLink'
import { reportMagicNumbers } from './rules/rrd/magicNumbers'
import { reportMultiAttributeElements } from './rules/vue-strong/multiAttributeElements'
import { reportElementSelectorsWithScoped } from './rules/vue-caution/elementSelectorsWithScoped'
import { reportIfWithoutCurlyBraces } from './rules/rrd/ifWithoutCurlyBraces'

export const reportRules = (groupBy: GroupBy) => {
let errors = 0
Expand Down Expand Up @@ -86,6 +87,7 @@ export const reportRules = (groupBy: GroupBy) => {
processOffenses(reportElseCondition)
processOffenses(reportFunctionSize)
processOffenses(reportHtmlLink)
processOffenses(reportIfWithoutCurlyBraces)
processOffenses(reportMagicNumbers)
processOffenses(reportParameterCount)
processOffenses(reportPlainScript)
Expand Down

0 comments on commit b124f4f

Please sign in to comment.