Repository: rrd108/vue-mess-detector
Pull request #65 (2024-07-19 01:59:52)

feat: add rrd short-variable-name rule


Patch:
diff --git a/README.md b/README.md
index deaf51a..b2996b6 100644
--- a/README.md
+++ b/README.md
@@ -153,3 +153,7 @@ Checks if functions inside `<script setup>` block are less than 20 lines of code
 ### Parameter Count (rrd)
 
 Checks if functions inside `<script setup>` block have less than 4 parameters. It handles regular and arrow functions.
+
+### Short Variable Name (rrd)
+
+Checks if variable names inside `<script setup>` block have less than 4 chars.
diff --git a/src/analyzer.ts b/src/analyzer.ts
index ef68f42..9a8a770 100644
--- a/src/analyzer.ts
+++ b/src/analyzer.ts
@@ -20,6 +20,7 @@ import { checkDirectiveShorthands, reportDirectiveShorthands } from './rules/vue
 import { checkTooManyProps, reportTooManyProps } from './rules/rrd/tooManyProps'
 import { checkFunctionSize, reportFunctionSize } from './rules/rrd/functionSize'
 import { checkParameterCount, reportParameterCount } from './rules/rrd/parameterCount'
+import { checkShortVariableName, reportShortVariableName } from './rules/rrd/shortVariableName'
 
 let filesCount = 0
 
@@ -86,6 +87,7 @@ export const analyze = (dir: string) => {
       checkTooManyProps(script, filePath)
       checkFunctionSize(script, filePath)
       checkParameterCount(script, filePath)
+      checkShortVariableName(script, filePath)
     }
 
     descriptor.styles.forEach(style => {
@@ -131,6 +133,7 @@ export const analyze = (dir: string) => {
   errors += reportTooManyProps()
   errors += reportFunctionSize()
   errors += reportParameterCount()
+  errors += reportShortVariableName()
 
   if (!errors) {
     console.log(`${BG_OK}No code smells detected!${BG_RESET}`)
diff --git a/src/rules/rrd/shortVariableName.ts b/src/rules/rrd/shortVariableName.ts
new file mode 100644
index 0000000..d3f51a1
--- /dev/null
+++ b/src/rules/rrd/shortVariableName.ts
@@ -0,0 +1,53 @@
+import { SFCScriptBlock } from "@vue/compiler-sfc";
+import { BG_RESET, BG_WARN, TEXT_WARN, TEXT_RESET, TEXT_INFO } from "../asceeCodes";
+
+type ShortVariableNameFile = {
+  filename: string;
+  variable: string;
+};
+
+const MIN_VARIABLE_NAME = 4; // completely rrd made-up number
+
+const shortVariableNameFile: ShortVariableNameFile[] = [];
+
+// Helper function to count non duplicated objects
+const getUniqueFilenameCount = (arr: ShortVariableNameFile[]) => {
+  const uniqueFilenames = new Set(arr.map((item) => item.filename));
+  return uniqueFilenames.size;
+};
+
+const checkShortVariableName = (script: SFCScriptBlock, filePath: string) => {
+  // Regular expression to match variable names
+  const regex = /\b(?:const|var|let)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
+
+  let match;
+  while ((match = regex.exec(script.content)) !== null) {
+    const variable = match[1];
+
+    if (variable.length < MIN_VARIABLE_NAME) {
+      shortVariableNameFile.push({ filename: filePath, variable });
+    }
+  }
+};
+
+const reportShortVariableName = () => {
+  if (shortVariableNameFile.length > 0) {
+    // Count only non duplicated objects (by its `filename` property)
+    // i.e if three variables have short name from the same file, it will only count as 1 file
+    // and not as 3 files
+    const fileCount = getUniqueFilenameCount(shortVariableNameFile);
+
+    console.log(
+      `\n${TEXT_INFO}rrd${TEXT_RESET} ${BG_WARN}variable names${BG_RESET} are too short in ${fileCount} files.`
+    );
+    console.log(
+      `👉 ${TEXT_WARN}Variable names must have a minimum length of ${MIN_VARIABLE_NAME}${TEXT_RESET}`
+    );
+    shortVariableNameFile.forEach((file) => {
+      console.log(`- ${file.filename} 🚨 ${BG_WARN}(${file.variable})${BG_RESET}`);
+    });
+  }
+  return shortVariableNameFile.length;
+};
+
+export { checkShortVariableName, reportShortVariableName };


Test patch:
diff --git a/src/rules/rrd/shortVariableName.test.ts b/src/rules/rrd/shortVariableName.test.ts
new file mode 100644
index 0000000..eb24800
--- /dev/null
+++ b/src/rules/rrd/shortVariableName.test.ts
@@ -0,0 +1,59 @@
+import { describe, expect, it, vi } from 'vitest';
+import { SFCScriptBlock } from '@vue/compiler-sfc';
+import { checkShortVariableName, reportShortVariableName } from './shortVariableName';
+import { BG_RESET, BG_WARN } from '../asceeCodes';
+
+const mockConsoleLog = vi.spyOn(console, 'log').mockImplementation(() => {});
+
+describe('shortVariableName', () => {
+  it('should not report files where variable names are not too short', () => {
+    const script = {
+      content: `
+        <script setup>
+        import { ref } from 'vue';
+        const isLoading = ref(false);
+        </script>
+      `
+    } as SFCScriptBlock;
+    const filename = 'not-short-variable-name.vue';
+    checkShortVariableName(script, filename);
+    expect(reportShortVariableName()).toBe(0);
+    expect(mockConsoleLog).not.toHaveBeenCalled();
+  })
+
+  it('should report files where one variable is too short', () => {
+    const script = {
+      content: `
+        <script setup>
+        let age = 10;
+        </script>
+      `
+    } as SFCScriptBlock;
+    const filename = 'short-variable-name.vue';
+    const variable = 'age';
+    checkShortVariableName(script, filename);
+    expect(reportShortVariableName()).toBe(1);
+    expect(mockConsoleLog).toHaveBeenCalled();
+    expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${filename} 🚨 ${BG_WARN}(${variable})${BG_RESET}`);
+  })
+  
+  it('should report files where more than one variable are too short', () => {
+    const script = {
+      content: `
+        <script setup>
+        import { ref } from 'vue';
+        const age = ref(10);
+        let gps = ref(null);
+        var isLoading = false;
+        const lng = 5;
+        </script>
+      `
+    } as SFCScriptBlock;
+    const filename = 'multiple-short-variable-name.vue';
+    const variable = 'lng';
+    checkShortVariableName(script, filename);
+    expect(reportShortVariableName()).toBe(4);
+    expect(mockConsoleLog).toHaveBeenCalled();
+    expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${filename} 🚨 ${BG_WARN}(${variable})${BG_RESET}`);
+  })
+})
\ No newline at end of file