Repository: rrd108/vue-mess-detector
Pull request #192 (2024-08-28 09:46:28)

added noVarDeclaration rule #172
### Summary
add rule (rrd) usage of var keyword #160
Add a new rule to disallow the use of var keyword for variable declarations

### Description
This change introduces a new rule in the codebase that enforces the use of const or let instead of the var keyword for variable declarations. The rule aims to modernize the code by avoiding the use of var, which has function scope and can lead to unexpected behavior due to its hoisting characteristics.

### Type of Change
<!-- Please select the options that best describe your changes -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [x] Documentation update





Patch:
diff --git a/.prettierrc b/.prettierrc
index 7690cbe..61d7171 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,6 +1,7 @@
 {
   "printWidth": 200,
   "singleQuote": true,
+  "quoteProps": "consistent",
   "semi": false,
   "arrowParens": "avoid"
 }
diff --git a/docs/rules/rrd/index.md b/docs/rules/rrd/index.md
index 6b7bd83..81ab145 100644
--- a/docs/rules/rrd/index.md
+++ b/docs/rules/rrd/index.md
@@ -1,6 +1,6 @@
 # 💻 rrd Rules
 
-These ruleset is the most opinionated with rules that are not part of the *offical* Vue style guide. They are based on the experience of this repo owner and are meant to help you write better Vue code.
+These ruleset is the most opinionated with rules that are not part of the _offical_ Vue style guide. They are based on the experience of this repo owner and are meant to help you write better Vue code.
 
 - [Cyclomatic Complexity](./cyclomatic-complexity.md)
 - [Deep Indentation](./deep-indentation.md)
@@ -11,6 +11,7 @@ These ruleset is the most opinionated with rules that are not part of the *offic
 - [Magic Numbers](./magic-numbers.md)
 - [Nested Ternary](./nested-ternary.md)
 - [No Prop Destructing](./no-prop-destructure.md)
+- [No Var Declaration](./no-var-declaration.md)
 - [Parameter Count](./parameter-count.md)
 - [Plain Script](./plain-script.md)
 - [Props Drilling](./props-drilling.md)
diff --git a/docs/rules/rrd/no-var-declaration.md b/docs/rules/rrd/no-var-declaration.md
new file mode 100644
index 0000000..18e6d2e
--- /dev/null
+++ b/docs/rules/rrd/no-var-declaration.md
@@ -0,0 +1,42 @@
+# Avoid Using var for Variable Declarations
+
+In modern JavaScript, especially within Vue components, it's crucial to avoid using `var` for variable declarations. The `var` keyword has several issues compared to `let` and `const`, including scope and hoisting problems that lead to bugs and unpredictable behavior.
+
+## 📖 What is `var`?
+
+The `var` keyword is used to declare variables in JavaScript. It was the original way to declare variables before `let` and `const` were introduced with ECMAScript 2015 (ES6).
+
+### Key Issues with `var`:
+
+- Vue’s reactivity system relies on modern JavaScript features to track changes and update the UI efficiently. `var`’s function-scoped nature and lack of block scope can interfere with Vue’s reactivity system, leading to problems where changes are not detected or where the code behaves unpredictably.
+- Code Maintainability: Using `var` makes code harder to maintain and understand. Developers often expect `let` and `const` to be used for variable declarations because they offer block-level scoping, which is more predictable and easier to reason about.
+
+## ❓ Why it's good to follow this rule?
+
+- **Predictable Behavior**: `let` and `const` provide block scope, which makes variable behavior more predictable and avoids issues related to variable hoisting and function scoping.
+
+- **Improved Reactivity**: Using `let` and `const` helps maintain the integrity of Vue’s reactivity system by avoiding potential pitfalls associated with `var`.
+
+- **Better Code Quality**: `let` and `const` promote clearer and more maintainable code and help prevent bugs that arise from the quirks of `var`.
+
+Incorrect Usage:
+
+## 😱 Examples of code for which this rule will throw a warning
+
+```vue
+<script setup>
+var x
+</script>
+```
+
+## 🤩 How to fix it?
+
+To ensure better scoping and avoid issues related to `var`, use `let` and `const` instead.
+
+Correct Usage:
+
+```vue
+<script setup>
+const x;
+</script>
+```
diff --git a/src/rules/rrd/noVarDeclaration.ts b/src/rules/rrd/noVarDeclaration.ts
new file mode 100644
index 0000000..59ab0ca
--- /dev/null
+++ b/src/rules/rrd/noVarDeclaration.ts
@@ -0,0 +1,43 @@
+import type { SFCScriptBlock } from '@vue/compiler-sfc'
+import { BG_RESET, BG_WARN, TEXT_INFO, TEXT_RESET, TEXT_WARN } from '../asceeCodes'
+import getLineNumber from '../getLineNumber'
+import type { FileCheckResult, Offense } from '../../types'
+
+const results: FileCheckResult[] = []
+
+const checkNoVarDeclaration = (script: SFCScriptBlock | null, filePath: string) => {
+  if (!script) {
+    return
+  }
+
+  const regex = /\bvar\s+(\w+(\s*=[^;]*)?|\{[^}]*\}(\s*=[^;]*)?)\s*;?/g
+  const matches = script.content.match(regex)
+
+  matches?.forEach((match) => {
+    const lineNumber = getLineNumber(script.content, match)
+    results.push({
+      filePath,
+      message: `line #${lineNumber} ${BG_WARN}Avoid using 'var' for variable declarations: ${match}${BG_RESET}`,
+    })
+  })
+}
+
+const reportNoVarDeclaration = () => {
+  const offenses: Offense[] = []
+
+  if (results.length > 0) {
+    results.forEach((result) => {
+      offenses.push({
+        file: result.filePath,
+        rule: `${TEXT_INFO}rrd ~ No Var Declaration${TEXT_RESET}`,
+        description: `👉 ${TEXT_WARN}Avoid var declaration, use const or let instead of that.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/no-var-declaration.html`,
+        message: `${result.message} 🚨`,
+      })
+    })
+  }
+  return offenses
+}
+
+const resetNoVarDeclaration = () => (results.length = 0)
+
+export { checkNoVarDeclaration, reportNoVarDeclaration, resetNoVarDeclaration }
diff --git a/src/rules/rules.ts b/src/rules/rules.ts
index 76e037a..475fda2 100644
--- a/src/rules/rules.ts
+++ b/src/rules/rules.ts
@@ -1,7 +1,19 @@
 export const RULES = {
-  'vue-caution': ['elementSelectorsWithScoped', 'implicitParentChildCommunication'],
-  'vue-essential': ['globalStyle', 'simpleProp', 'singleNameComponent', 'vforNoKey', 'vifWithVfor'],
-  'vue-recommended': ['elementAttributeOrder', 'topLevelElementOrder'],
+  'vue-caution': [
+    'elementSelectorsWithScoped',
+    'implicitParentChildCommunication',
+  ],
+  'vue-essential': [
+    'globalStyle',
+    'simpleProp',
+    'singleNameComponent',
+    'vforNoKey',
+    'vifWithVfor',
+  ],
+  'vue-recommended': [
+    'elementAttributeOrder',
+    'topLevelElementOrder',
+  ],
   'vue-strong': [
     'componentFilenameCasing',
     'componentFiles',
@@ -24,6 +36,7 @@ export const RULES = {
     'magicNumbers',
     'nestedTernary',
     'noPropDestructure',
+    'noVarDeclaration',
     'parameterCount',
     'plainScript',
     'propsDrilling',
diff --git a/src/rulesReport.ts b/src/rulesReport.ts
index 5f1b706..dcfdef0 100644
--- a/src/rulesReport.ts
+++ b/src/rulesReport.ts
@@ -65,6 +65,7 @@ export const reportRules = (groupBy: GroupBy, orderBy: OrderBy, level: OutputLev
   processOffenses(reportMagicNumbers)
   processOffenses(reportNestedTernary)
   processOffenses(reportNoPropDestructure)
+  processOffenses(reportNoVarDeclaration)
   processOffenses(reportParameterCount)
   processOffenses(reportPlainScript)
   processOffenses(reportPropsDrilling)


Test patch:
diff --git a/src/rules/rrd/noVarDeclaration.test.ts b/src/rules/rrd/noVarDeclaration.test.ts
new file mode 100644
index 0000000..35a62a4
--- /dev/null
+++ b/src/rules/rrd/noVarDeclaration.test.ts
@@ -0,0 +1,85 @@
+import { beforeEach, describe, expect, it } from 'vitest'
+import type { SFCScriptBlock } from '@vue/compiler-sfc'
+import { BG_RESET, BG_WARN, TEXT_INFO, TEXT_RESET, TEXT_WARN } from '../asceeCodes'
+import { checkNoVarDeclaration, reportNoVarDeclaration, resetNoVarDeclaration } from './noVarDeclaration'
+
+describe('checkNoVarDeclaration', () => {
+  beforeEach(() => {
+    resetNoVarDeclaration()
+  })
+
+  it('should not report files with no var declarations', () => {
+    const script = {
+      content: `
+        const x = 5;
+        let y = 10;
+        function test() {
+          const z = x + y;
+        }
+      `,
+    } as SFCScriptBlock
+    const fileName = 'noVarDeclaration.vue'
+    checkNoVarDeclaration(script, fileName)
+    expect(reportNoVarDeclaration().length).toBe(0)
+    expect(reportNoVarDeclaration()).toStrictEqual([])
+  })
+
+  it('should report files with a single var declaration', () => {
+    const script = {
+      content: `
+        var x = 5;
+      `,
+    } as SFCScriptBlock
+    const fileName = 'noVarDeclaration-single.vue'
+    checkNoVarDeclaration(script, fileName)
+    expect(reportNoVarDeclaration().length).toBe(1)
+    expect(reportNoVarDeclaration()).toStrictEqual([
+      {
+        file: fileName,
+        rule: `${TEXT_INFO}rrd ~ No Var Declaration${TEXT_RESET}`,
+        description: `👉 ${TEXT_WARN}Avoid var declaration, use const or let instead of that.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/no-var-declaration.html`,
+        message: `line #2 ${BG_WARN}Avoid using 'var' for variable declarations: var x = 5;${BG_RESET} 🚨`,
+      },
+    ])
+  })
+
+  it('should report files with multiple var declarations', () => {
+    const script = {
+      content: `
+        var a = 1;
+        var b = 2;
+        var c = 3;
+        var d = a + b;
+      `,
+    } as SFCScriptBlock
+    const fileName = 'noVarDeclaration-multiple.vue'
+    checkNoVarDeclaration(script, fileName)
+    expect(reportNoVarDeclaration().length).toBe(4)
+    expect(reportNoVarDeclaration()).toStrictEqual([
+      {
+        file: fileName,
+        rule: `${TEXT_INFO}rrd ~ No Var Declaration${TEXT_RESET}`,
+        description: `👉 ${TEXT_WARN}Avoid var declaration, use const or let instead of that.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/no-var-declaration.html`,
+        message: `line #2 ${BG_WARN}Avoid using 'var' for variable declarations: var a = 1;${BG_RESET} 🚨`,
+      },
+      {
+        file: fileName,
+        rule: `${TEXT_INFO}rrd ~ No Var Declaration${TEXT_RESET}`,
+        description: `👉 ${TEXT_WARN}Avoid var declaration, use const or let instead of that.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/no-var-declaration.html`,
+        message: `line #3 ${BG_WARN}Avoid using 'var' for variable declarations: var b = 2;${BG_RESET} 🚨`,
+      },
+      {
+        file: fileName,
+        rule: `${TEXT_INFO}rrd ~ No Var Declaration${TEXT_RESET}`,
+        description: `👉 ${TEXT_WARN}Avoid var declaration, use const or let instead of that.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/no-var-declaration.html`,
+        message: `line #4 ${BG_WARN}Avoid using 'var' for variable declarations: var c = 3;${BG_RESET} 🚨`,
+      },
+      {
+        file: fileName,
+        rule: `${TEXT_INFO}rrd ~ No Var Declaration${TEXT_RESET}`,
+        description: `👉 ${TEXT_WARN}Avoid var declaration, use const or let instead of that.${TEXT_RESET} See: https://vue-mess-detector.webmania.cc/rules/rrd/no-var-declaration.html`,
+        message: `line #5 ${BG_WARN}Avoid using 'var' for variable declarations: var d = a + b;${BG_RESET} 🚨`,
+      },
+    ])
+  })
+})