Skip to content

Commit

Permalink
Merge branch 'main' into feature/parameter-count
Browse files Browse the repository at this point in the history
  • Loading branch information
David-Pena authored Jul 17, 2024
2 parents 585f047 + b51d6a2 commit 825b21d
Show file tree
Hide file tree
Showing 10 changed files with 615 additions and 390 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Run Tests

on:
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '18' # or another version you prefer

- name: Install dependencies
run: yarn install

- name: Run tests
run: yarn test
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ Checks if there are any `else` condition in the `<script>` block. This is a code

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.
8 changes: 0 additions & 8 deletions dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,4 @@ git status
./deploy.sh minor publish
```

## JSR

`jsr.json` version number should be bumped.

```
npx jsr publish
```

TODO: set up GH Action https://jsr.io/docs/publishing-packages#publishing-from-github-actions
709 changes: 361 additions & 348 deletions dist/vue-mess-detector.es.js

Large diffs are not rendered by default.

66 changes: 34 additions & 32 deletions dist/vue-mess-detector.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@rrd/vue-mess-detector",
"version": "0.18.2",
"version": "0.19.0",
"exports": "./src/index.ts"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-mess-detector",
"version": "0.18.2",
"version": "0.19.0",
"description": "A static code analysis tool for detecting code smells and best practice violations in Vue.js and Nuxt.js projects",
"type": "module",
"main": "dist/vue-mess-detector.es.js",
Expand Down
3 changes: 3 additions & 0 deletions src/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { checkQuotedAttributeValues, reportQuotedAttributeValues } from './rules
import { checkSelfClosingComponents, reportSelfClosingComponents } from './rules/vue-strong/selfClosingComponents'
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 @@ -83,6 +84,7 @@ export const analyze = (dir: string) => {
checkCyclomaticComplexity(script, filePath)
checkElseCondition(script, filePath)
checkTooManyProps(script, filePath)
checkFunctionSize(script, filePath)
checkParameterCount(script, filePath)
}

Expand Down Expand Up @@ -127,6 +129,7 @@ export const analyze = (dir: string) => {
errors += reportCyclomaticComplexity()
errors += reportElseCondition()
errors += reportTooManyProps()
errors += reportFunctionSize()
errors += reportParameterCount()

if (!errors) {
Expand Down
146 changes: 146 additions & 0 deletions src/rules/rrd/functionSize.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { describe, expect, it, vi } from 'vitest'
import { SFCScriptBlock } from '@vue/compiler-sfc'
import { checkFunctionSize, reportFunctionSize } from './functionSize'
import { BG_RESET, BG_WARN } from '../asceeCodes'

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

describe('checkFunctionSize', () => {
it('should not report files where functions do not exceed the recommended limit', () => {
const script = {
content: `
<script setup>
function greeting(msg) {
if (!msg) {
return
}
return {
message: msg.trim()
}
}
</script>
`
} as SFCScriptBlock;
const fileName = 'function-size.vue';
checkFunctionSize(script, fileName);
expect(reportFunctionSize()).toBe(0);
expect(mockConsoleLog).not.toHaveBeenCalled();
})

it('should report files with one function exceeding the limit', () => {
const script = {
content: `
<script setup>
function dummyRegularFunction() {
const firstName = 'john';
const lastName = 'doe';
let age = 30;
if (age < 18) {
console.log('Too young for this function!')
} else {
console.log('Hello ', firstName)
}
//Some more lines of code
const hobbies = ['reading', 'gaming', 'cooking'];
for (const hobby of hobbies) {
console.log('I enjoy ', hobby);
}
// Even more lines...
const getRandomNumber = () => Math.floor(Math.random() * 100);
const randomNum = getRandomNumber();
console.log('Random number: ', randomNum);
// And a final line
return 'Function execution complete!';
}
const sayHello = () => {
console.log(getGreeting());
}
</script>
`
} as SFCScriptBlock;
const fileName = 'function-size.vue';
const funcName = 'dummyRegularFunction';
checkFunctionSize(script, fileName);
expect(reportFunctionSize()).toBe(1);
expect(mockConsoleLog).toHaveBeenCalled();
expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${fileName} 🚨 ${BG_WARN}(${funcName})${BG_RESET}`)
})

it('should report files with two functions exceeding the limit', () => {
const script = {
content: `
<script setup>
function dummyRegularFunction(name = 'Hi') {
if (!name) {
return
}
let message = '';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
message = 'Just testing this feature';
return message;
}
const dummyArrowFunction = (name) => {
const firstName = 'john';
const lastName = 'doe';
let age = 30;
if (age < 18) {
console.log('Too young for this function!')
} else {
console.log('Hello ', firstName)
}
// Some more lines of code
const hobbies = ['reading', 'gaming', 'cooking'];
for (const hobby of hobbies) {
console.log('I enjoy ', hobby);
}
// Even more lines...
const getRandomNumber = () => Math.floor(Math.random() * 100);
const randomNum = getRandomNumber();
console.log('Random number: ', randomNum);
// And a final line
return 'Function execution complete!';
}
</script>
`
} as SFCScriptBlock;
const fileName = 'function-size.vue';
const funcName = 'dummyArrowFunction';
checkFunctionSize(script, fileName);
expect(reportFunctionSize()).toBe(3);
expect(mockConsoleLog).toHaveBeenCalled();
expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${fileName} 🚨 ${BG_WARN}(${funcName})${BG_RESET}`)
})
})
42 changes: 42 additions & 0 deletions src/rules/rrd/functionSize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SFCScriptBlock } from '@vue/compiler-sfc';
import { BG_RESET, BG_WARN, TEXT_WARN, TEXT_RESET, TEXT_INFO } from '../asceeCodes'

const functionSizeFiles: { filename: string, funcName: string }[] = [];

const MAX_FUNCTION_LENGTH = 20 // completely rrd made-up number

const checkFunctionSize = (script: SFCScriptBlock, filePath: string) => {
// Regular expression to match function definitions (both regular and arrow functions)
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) {
/*
We use match[1] and match[2] for regular functions
and match[5] and match[6] for arrow functions
*/
const funcName = match[1] || match[5];
const funcBody = match[2] || match[6];

// Check if the function block has more than `MAX_FUNCTION_LENGTH` lines
const lineCount = funcBody.split('\n').length;
if (lineCount > MAX_FUNCTION_LENGTH) {
functionSizeFiles.push({ filename: filePath, funcName });
}
}
}

const reportFunctionSize = () => {
if (functionSizeFiles.length > 0) {
console.log(
`\n${TEXT_INFO}rrd${TEXT_RESET} ${BG_WARN}function size${BG_RESET} exceeds recommended limit in ${functionSizeFiles.length} files.`
)
console.log(`👉 ${TEXT_WARN}Functions must be shorter than ${MAX_FUNCTION_LENGTH} lines${TEXT_RESET}`)
functionSizeFiles.forEach(file => {
console.log(`- ${file.filename} 🚨 ${BG_WARN}(${file.funcName})${BG_RESET}`)
})
}
return functionSizeFiles.length
}

export { checkFunctionSize, reportFunctionSize };

0 comments on commit 825b21d

Please sign in to comment.