Skip to content

Commit bf5829c

Browse files
Add new vue/require-prop-comment rule (#2019)
* require-prop-comment * add type arg * add jsdoc type * edit rule * npm run update运行结果 * npm run update edit * Modify according to the requirements during consolidation * edit test * delete only one check * delete template * edit md * Lint * Improve docs * Only check last preceding comment * Use message IDs * Rename unlimited → any * Fix docs * edit rules * edit schema type * update readme.md * add rules * add rules Co-authored-by: Flo Edelmann <[email protected]>
1 parent 2bdcc51 commit bf5829c

File tree

5 files changed

+538
-0
lines changed

5 files changed

+538
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ For example:
260260
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | :bulb: | :hammer: |
261261
| [vue/require-expose](./require-expose.md) | require declare public properties using `expose` | :bulb: | :hammer: |
262262
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | | :hammer: |
263+
| [vue/require-prop-comment](./require-prop-comment.md) | require props to have a comment | | :hammer: |
263264
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: | :lipstick: |
264265
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | | :hammer: |
265266
| [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: | :hammer: |

docs/rules/require-prop-comment.md

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/require-prop-comment
5+
description: require props to have a comment
6+
---
7+
# vue/require-prop-comment
8+
9+
> require props to have a comment
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule enforces that every prop has a comment that documents it.
16+
17+
<eslint-code-block :rules="{'vue/require-prop-comment': ['error']}">
18+
19+
```vue
20+
<script>
21+
export default defineComponent({
22+
props: {
23+
// ✓ GOOD
24+
25+
/** JSDoc comment */
26+
a: Number,
27+
28+
// ✗ BAD
29+
30+
// line comment
31+
b: Number,
32+
33+
/* block comment */
34+
c: Number,
35+
36+
d: Number,
37+
}
38+
})
39+
</script>
40+
```
41+
42+
</eslint-code-block>
43+
44+
## :wrench: Options
45+
46+
```json
47+
{
48+
"vue/require-prop-comment": ["error", {
49+
"type": "JSDoc"
50+
}]
51+
}
52+
```
53+
54+
- `type` ... Type of comment. Default is `"JSDoc"`
55+
- `"JSDoc"` ... Only JSDoc comment are allowed.
56+
- `"line"` ... Only line comment are allowed.
57+
- `"block"` ... Only block comment are allowed.
58+
- `"any"` ... All comment types are allowed.
59+
60+
### `"type": "block"`
61+
62+
<eslint-code-block :rules="{'vue/require-prop-comment': ['error', {type: 'block'}]}">
63+
64+
```vue
65+
<script setup>
66+
// ✓ GOOD
67+
const goodProps = defineProps({
68+
/* block comment */
69+
a: Number,
70+
})
71+
72+
// ✗ BAD
73+
const badProps = defineProps({
74+
/** JSDoc comment */
75+
b: Number,
76+
77+
// line comment
78+
c: Number,
79+
80+
d: Number,
81+
})
82+
</script>
83+
```
84+
85+
</eslint-code-block>
86+
87+
### `"type": "line"`
88+
89+
<eslint-code-block :rules="{'vue/require-prop-comment': ['error', {type: 'line'}]}">
90+
91+
```vue
92+
<script setup>
93+
// ✓ GOOD
94+
const goodProps = defineProps({
95+
// line comment
96+
a: Number,
97+
})
98+
99+
// ✗ BAD
100+
const badProps = defineProps({
101+
/** JSDoc comment */
102+
b: Number,
103+
104+
/* block comment */
105+
c: Number,
106+
107+
d: Number,
108+
})
109+
</script>
110+
```
111+
112+
</eslint-code-block>
113+
114+
### `"type": "any"`
115+
116+
<eslint-code-block :rules="{'vue/require-prop-comment': ['error', {type: 'any'}]}">
117+
118+
```vue
119+
<script setup>
120+
// ✓ GOOD
121+
const goodProps = defineProps({
122+
/** JSDoc comment */
123+
a: Number,
124+
125+
/* block comment */
126+
b: Number,
127+
128+
// line comment
129+
c: Number,
130+
})
131+
132+
// ✗ BAD
133+
const badProps = defineProps({
134+
d: Number,
135+
})
136+
</script>
137+
```
138+
139+
</eslint-code-block>
140+
141+
## :mag: Implementation
142+
143+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-prop-comment.js)
144+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-prop-comment.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ module.exports = {
176176
'require-explicit-emits': require('./rules/require-explicit-emits'),
177177
'require-expose': require('./rules/require-expose'),
178178
'require-name-property': require('./rules/require-name-property'),
179+
'require-prop-comment': require('./rules/require-prop-comment'),
179180
'require-prop-type-constructor': require('./rules/require-prop-type-constructor'),
180181
'require-prop-types': require('./rules/require-prop-types'),
181182
'require-render-return': require('./rules/require-render-return'),

lib/rules/require-prop-comment.js

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* @author CZB
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
module.exports = {
10+
meta: {
11+
type: 'suggestion',
12+
docs: {
13+
description: 'require props to have a comment',
14+
categories: undefined,
15+
url: 'https://eslint.vuejs.org/rules/require-prop-comment.html'
16+
},
17+
fixable: null,
18+
schema: [
19+
{
20+
type: 'object',
21+
properties: {
22+
type: { enum: ['JSDoc', 'line', 'block', 'any'] }
23+
},
24+
additionalProperties: false
25+
}
26+
],
27+
messages: {
28+
requireAnyComment: 'The "{{name}}" property should have a comment.',
29+
requireLineComment: 'The "{{name}}" property should have a line comment.',
30+
requireBlockComment:
31+
'The "{{name}}" property should have a block comment.',
32+
requireJSDocComment:
33+
'The "{{name}}" property should have a JSDoc comment.'
34+
}
35+
},
36+
/** @param {RuleContext} context */
37+
create(context) {
38+
/** @type {{type?: "JSDoc" | "line" | "block" | "any"}|undefined} */
39+
const schema = context.options[0]
40+
const type = (schema && schema.type) || 'JSDoc'
41+
42+
const sourceCode = context.getSourceCode()
43+
44+
/** @param {Comment | undefined} comment */
45+
const verifyBlock = (comment) =>
46+
comment && comment.type === 'Block' && comment.value.charAt(0) !== '*'
47+
? undefined
48+
: 'requireBlockComment'
49+
50+
/** @param {Comment | undefined} comment */
51+
const verifyLine = (comment) =>
52+
comment && comment.type === 'Line' ? undefined : 'requireLineComment'
53+
54+
/** @param {Comment | undefined} comment */
55+
const verifyAny = (comment) => (comment ? undefined : 'requireAnyComment')
56+
57+
/** @param {Comment | undefined} comment */
58+
const verifyJSDoc = (comment) =>
59+
comment && comment.type === 'Block' && comment.value.charAt(0) === '*'
60+
? undefined
61+
: 'requireJSDocComment'
62+
63+
/**
64+
* @param {import('../utils').ComponentProp[]} props
65+
*/
66+
function verifyProps(props) {
67+
for (const prop of props) {
68+
if (!prop.propName) {
69+
continue
70+
}
71+
72+
const precedingComments = sourceCode.getCommentsBefore(prop.node)
73+
const lastPrecedingComment =
74+
precedingComments.length > 0
75+
? precedingComments[precedingComments.length - 1]
76+
: undefined
77+
78+
/** @type {string|undefined} */
79+
let messageId
80+
81+
switch (type) {
82+
case 'block':
83+
messageId = verifyBlock(lastPrecedingComment)
84+
break
85+
case 'line':
86+
messageId = verifyLine(lastPrecedingComment)
87+
break
88+
case 'any':
89+
messageId = verifyAny(lastPrecedingComment)
90+
break
91+
default:
92+
messageId = verifyJSDoc(lastPrecedingComment)
93+
break
94+
}
95+
96+
if (!messageId) {
97+
continue
98+
}
99+
100+
context.report({
101+
node: prop.node,
102+
messageId,
103+
data: {
104+
name: prop.propName
105+
}
106+
})
107+
}
108+
}
109+
110+
return utils.compositingVisitors(
111+
utils.defineScriptSetupVisitor(context, {
112+
onDefinePropsEnter(_node, props) {
113+
verifyProps(props)
114+
}
115+
}),
116+
utils.defineVueVisitor(context, {
117+
onVueObjectEnter(node) {
118+
verifyProps(utils.getComponentPropsFromOptions(node))
119+
}
120+
})
121+
)
122+
}
123+
}

0 commit comments

Comments
 (0)