Skip to content

Commit

Permalink
Merge pull request #60 from David-Pena/feature/parameter-count
Browse files Browse the repository at this point in the history
feat: add rrd parameter-count rule
  • Loading branch information
rrd108 authored Jul 17, 2024
2 parents b51d6a2 + 825b21d commit cecb5a1
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,7 @@ Checks if the component got more then 5 props.
### Function Size (rrd)

Checks if functions inside `<script setup>` block are less than 20 lines of code. It handles regular and arrow functions.

### Parameter Count (rrd)

Checks if functions inside `<script setup>` block have less than 4 parameters. It handles regular and arrow functions.
3 changes: 3 additions & 0 deletions src/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { checkSelfClosingComponents, reportSelfClosingComponents } from './rules
import { checkDirectiveShorthands, reportDirectiveShorthands } from './rules/vue-strong/directiveShorthands'
import { checkTooManyProps, reportTooManyProps } from './rules/rrd/tooManyProps'
import { checkFunctionSize, reportFunctionSize } from './rules/rrd/functionSize'
import { checkParameterCount, reportParameterCount } from './rules/rrd/parameterCount'

let filesCount = 0

Expand Down Expand Up @@ -84,6 +85,7 @@ export const analyze = (dir: string) => {
checkElseCondition(script, filePath)
checkTooManyProps(script, filePath)
checkFunctionSize(script, filePath)
checkParameterCount(script, filePath)
}

descriptor.styles.forEach(style => {
Expand Down Expand Up @@ -128,6 +130,7 @@ export const analyze = (dir: string) => {
errors += reportElseCondition()
errors += reportTooManyProps()
errors += reportFunctionSize()
errors += reportParameterCount()

if (!errors) {
console.log(`${BG_OK}No code smells detected!${BG_RESET}`)
Expand Down
73 changes: 73 additions & 0 deletions src/rules/rrd/parameterCount.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { describe, expect, it, vi } from 'vitest'
import { SFCScriptBlock } from '@vue/compiler-sfc'
import { checkParameterCount, reportParameterCount } from './parameterCount'
import { BG_RESET, BG_WARN } from '../asceeCodes'

const mockConsoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})

describe('checkParameterCount', () => {
it('should not report files where functions do not exceed the recommended limit', () => {
const script = {
content: `
<script setup>
function dummyFuncOne() {
return 'One'
}
</script>
`
} as SFCScriptBlock;
const filename = 'no-parameters.vue';
checkParameterCount(script, filename);
expect(reportParameterCount()).toBe(0);
expect(mockConsoleLog).not.toHaveBeenCalled();
})

it('should report files where one function exceeds the recommended limit', () => {
const script = {
content: `
<script setup>
function dummyFuncOne() {
return 'One'
}
const dummyFuncTwo = (param1, param2, param3, param4) => {}
</script>
`
} as SFCScriptBlock;
const filename = 'one-parameter.vue';
const funcName = 'dummyFuncTwo';
const paramsCount = 4;
checkParameterCount(script, filename);
expect(reportParameterCount()).toBe(1);
expect(mockConsoleLog).toHaveBeenCalled();
expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${BG_WARN}${funcName}${BG_RESET} in file ${filename} 🚨 ${BG_WARN}(${paramsCount})${BG_RESET}`)

})

it('should report files where multiple functions exceed the recommended limit', () => {
const script = {
content: `
<script setup>
function dummyFuncOne(param1, param2, param3, param4, param5) {
return 'One'
}
const dummyFuncTwo = (param1, param2, param3, param4) => {
return 'Two'
}
const dummyFuncThree = (param1, param2, param3) => {
return 'Three'
}
</script>
`
} as SFCScriptBlock;
const filename = 'multiple-parameters.vue';
const funcName = 'dummyFuncTwo';
const paramsCount = 4;
checkParameterCount(script, filename);
expect(reportParameterCount()).toBe(3);
expect(mockConsoleLog).toHaveBeenCalled();
expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${BG_WARN}${funcName}${BG_RESET} in file ${filename} 🚨 ${BG_WARN}(${paramsCount})${BG_RESET}`)
})
})
44 changes: 44 additions & 0 deletions src/rules/rrd/parameterCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { SFCScriptBlock } from '@vue/compiler-sfc';
import { BG_RESET, BG_WARN, TEXT_WARN, TEXT_RESET, TEXT_INFO } from '../asceeCodes'

const parameterCountFiles: { filename: string, funcName: string, paramsCount: number }[] = [];

const MAX_PARAMETER_COUNT = 3 // completely rrd made-up number

// Function used in both scenarios (regular and arrow function) to count parameters
const checkParameters = (funcName: string, params: string, filePath: string) => {
const paramsArray = params.split(',').map(param => param.trim()).filter(param => param.length > 0);
if (paramsArray.length > MAX_PARAMETER_COUNT) {
parameterCountFiles.push({ filename: filePath, funcName, paramsCount: paramsArray.length });
}
}

const checkParameterCount = (script: SFCScriptBlock, filePath: string) => {
// regular expression to match both regular and arrow functions and capture their params
const regex = /function\s+([a-zA-Z0-9_$]+)\s*\(([^)]*)\)\s*{|const\s+([a-zA-Z0-9_$]+)\s*=\s*\(([^)]*)\)\s*=>\s*{/g;
let match;

while ((match = regex.exec(script.content)) !== null) {
if (match[1]) { // Regular function
checkParameters(match[1], match[2], filePath); // match[2] are the params for current regular function
} else if (match[3]) { // Arrow function
checkParameters(match[3], match[4], filePath) // match[4] are the params for current arrow function
}
}
}

const reportParameterCount = () => {
if (parameterCountFiles.length > 0) {
console.log(
`\n${TEXT_INFO}rrd${TEXT_RESET} ${BG_WARN}parameter count${BG_RESET} exceeds recommended limit in ${parameterCountFiles.length} files.`
)
console.log(`👉 ${TEXT_WARN}Max number of function parameters should be ${MAX_PARAMETER_COUNT}${TEXT_RESET}`);
parameterCountFiles.forEach(file => {
console.log(`- ${BG_WARN}${file.funcName}${BG_RESET} in file ${file.filename} 🚨 ${BG_WARN}(${file.paramsCount})${BG_RESET}`);
})
}

return parameterCountFiles.length;
}

export { checkParameterCount, reportParameterCount };

0 comments on commit cecb5a1

Please sign in to comment.