Skip to content

Commit 0fc1107

Browse files
committed
feat: implement rule for directive shorthands fix #36
1 parent 3822b2b commit 0fc1107

File tree

7 files changed

+420
-307
lines changed

7 files changed

+420
-307
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
node_modules
2+
test

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,14 @@ See: https://vuejs.org/style-guide/rules-strongly-recommended.html#simple-expres
107107
### Quoted Attribute Values (vue-strong)
108108

109109
Checks if the template is using quoted attribute values.
110-
See:https://vuejs.org/style-guide/rules-strongly-recommended.html#quoted-attribute-values
110+
See: https://vuejs.org/style-guide/rules-strongly-recommended.html#quoted-attribute-values
111+
112+
### Directive Shorthands (vue-strong)
113+
114+
Checks if the template is using directive shorthands.
115+
See: https://vuejs.org/style-guide/rules-strongly-recommended.html#directive-shorthands
116+
117+
> This rule enforces using directive shorthands for better readability. While the official style guide allows using shorthands "**always** or **never**", this rule will flag non-shorthands as code smell.
111118
112119
### Script Length (rrd)
113120

dist/vue-mess-detector.es.js

Lines changed: 292 additions & 277 deletions
Large diffs are not rendered by default.

dist/vue-mess-detector.umd.js

Lines changed: 29 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/analyzer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { checkPropNameCasing, reportPropNameCasing } from './rules/vue-strong/pr
1616
import { checkTemplateSimpleExpression, reportTemplateSimpleExpression } from './rules/vue-strong/templateSimpleExpression'
1717
import { checkQuotedAttributeValues, reportQuotedAttributeValues } from './rules/vue-strong/quotedAttribueValues'
1818
import { checkSelfClosingComponents, reportSelfClosingComponents } from './rules/vue-strong/selfClosingComponents'
19+
import { checkDirectiveShorthands, reportDirectiveShorthands } from './rules/vue-strong/directiveShorthands'
1920

2021
let filesCount = 0
2122

@@ -54,7 +55,6 @@ export const analyze = (dir: string) => {
5455
const files: string[] = []
5556

5657
walkSync(dir, filePath => {
57-
// skip app.vue and App.vue
5858
if (filePath.includes('App.vue') || filePath.includes('app.vue')) {
5959
return
6060
}
@@ -93,6 +93,7 @@ export const analyze = (dir: string) => {
9393
checkSelfClosingComponents(descriptor, filePath)
9494
checkTemplateSimpleExpression(descriptor.template, filePath)
9595
checkQuotedAttributeValues(descriptor, filePath)
96+
checkDirectiveShorthands(descriptor, filePath)
9697
}
9798
})
9899

@@ -111,6 +112,7 @@ export const analyze = (dir: string) => {
111112
errors += reportPropNameCasing()
112113
errors += reportTemplateSimpleExpression()
113114
errors += reportQuotedAttributeValues()
115+
errors += reportDirectiveShorthands()
114116

115117
// vue-reccomended rules
116118
// vue-caution rules
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { SFCDescriptor, SFCTemplateBlock } from '@vue/compiler-sfc'
3+
import { checkDirectiveShorthands, reportDirectiveShorthands } from './directiveShorthands'
4+
import { BG_RESET, BG_WARN } from '../asceeCodes'
5+
6+
const mockConsoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})
7+
8+
describe('checkDirectiveShorthands', () => {
9+
it('should not report files where directive shorthands are used', () => {
10+
const template = `<template #header>
11+
<input
12+
:value="newTodoText"
13+
@input="addTodo"
14+
>
15+
</template>`
16+
const descriptor = {
17+
source: template,
18+
template: {
19+
content: template,
20+
},
21+
} as SFCDescriptor
22+
const fileName = 'directive-shorthands.vue'
23+
checkDirectiveShorthands(descriptor, fileName)
24+
expect(reportDirectiveShorthands()).toBe(0)
25+
expect(mockConsoleLog).not.toHaveBeenCalled()
26+
})
27+
28+
it('should report files where directive shorthands are not used', () => {
29+
const template = `<template v-slot:header>
30+
<input
31+
v-bind:value="newTodoText"
32+
v-on:input="addTodo"
33+
>
34+
</template>`
35+
const descriptor = {
36+
source: template,
37+
template: {
38+
content: template,
39+
},
40+
} as SFCDescriptor
41+
const fileName = 'no-directive-shorthands.vue'
42+
checkDirectiveShorthands(descriptor, fileName)
43+
expect(reportDirectiveShorthands()).toBe(3)
44+
expect(mockConsoleLog).toHaveBeenCalled()
45+
expect(mockConsoleLog).toHaveBeenLastCalledWith(`- ${fileName}:4 ${BG_WARN}v-on${BG_RESET} 🚨`)
46+
})
47+
})
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { SFCDescriptor } from '@vue/compiler-sfc'
2+
import { BG_RESET, TEXT_WARN, TEXT_RESET, BG_ERR, TEXT_INFO, BG_WARN } from '../asceeCodes'
3+
import getLineNumber from '../getLineNumber'
4+
5+
const directiveShorthandsTargets: { message: string }[] = []
6+
const directiveShorthandsFiles: { filrPath: string }[] = []
7+
8+
const directivesToCheck = ['v-slot', 'v-bind', 'v-on']
9+
10+
const checkDirectiveShorthands = (descriptor: SFCDescriptor, filePath: string) => {
11+
const template = descriptor.template! // it is exists otherwise the function is not called at all
12+
13+
directivesToCheck.forEach(directive => {
14+
if (template.content.includes(`${directive}:`)) {
15+
const lineNumber = getLineNumber(descriptor.source, directive)
16+
directiveShorthandsTargets.push({ message: `${filePath}:${lineNumber} ${BG_WARN}${directive}${BG_RESET}` })
17+
18+
if (!directiveShorthandsFiles.some(file => file.filrPath === filePath)) {
19+
directiveShorthandsFiles.push({ filrPath: filePath })
20+
}
21+
}
22+
})
23+
}
24+
25+
const reportDirectiveShorthands = () => {
26+
if (directiveShorthandsTargets.length > 0) {
27+
console.log(
28+
`\n${TEXT_INFO}vue-strong${TEXT_RESET} ${BG_ERR}Directive shorthands not used${BG_RESET} in ${directiveShorthandsFiles.length} files.`
29+
)
30+
console.log(
31+
`👉 ${TEXT_WARN}Use ":" for v-bind:, "@" for v-on: and "#" for v-slot.${TEXT_RESET} See: https://vuejs.org/style-guide/rules-strongly-recommended.html#directive-shorthands`
32+
)
33+
directiveShorthandsTargets.forEach(file => {
34+
console.log(`- ${file.message} 🚨`)
35+
})
36+
}
37+
return directiveShorthandsTargets.length
38+
}
39+
40+
export { checkDirectiveShorthands, reportDirectiveShorthands }

0 commit comments

Comments
 (0)