Skip to content

Commit

Permalink
Add new vue/require-prop-comment rule (#2019)
Browse files Browse the repository at this point in the history
* 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 <florian-edelmann@online.de>
  • Loading branch information
czb3279338858 and FloEdelmann committed Nov 2, 2022
1 parent 2bdcc51 commit bf5829c
Show file tree
Hide file tree
Showing 5 changed files with 538 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -260,6 +260,7 @@ For example:
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | :bulb: | :hammer: |
| [vue/require-expose](./require-expose.md) | require declare public properties using `expose` | :bulb: | :hammer: |
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | | :hammer: |
| [vue/require-prop-comment](./require-prop-comment.md) | require props to have a comment | | :hammer: |
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: | :lipstick: |
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | | :hammer: |
| [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: | :hammer: |
Expand Down
144 changes: 144 additions & 0 deletions docs/rules/require-prop-comment.md
@@ -0,0 +1,144 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/require-prop-comment
description: require props to have a comment
---
# vue/require-prop-comment

> require props to have a comment
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

## :book: Rule Details

This rule enforces that every prop has a comment that documents it.

<eslint-code-block :rules="{'vue/require-prop-comment': ['error']}">

```vue
<script>
export default defineComponent({
props: {
// ✓ GOOD
/** JSDoc comment */
a: Number,
// ✗ BAD
// line comment
b: Number,
/* block comment */
c: Number,
d: Number,
}
})
</script>
```

</eslint-code-block>

## :wrench: Options

```json
{
"vue/require-prop-comment": ["error", {
"type": "JSDoc"
}]
}
```

- `type` ... Type of comment. Default is `"JSDoc"`
- `"JSDoc"` ... Only JSDoc comment are allowed.
- `"line"` ... Only line comment are allowed.
- `"block"` ... Only block comment are allowed.
- `"any"` ... All comment types are allowed.

### `"type": "block"`

<eslint-code-block :rules="{'vue/require-prop-comment': ['error', {type: 'block'}]}">

```vue
<script setup>
// ✓ GOOD
const goodProps = defineProps({
/* block comment */
a: Number,
})
// ✗ BAD
const badProps = defineProps({
/** JSDoc comment */
b: Number,
// line comment
c: Number,
d: Number,
})
</script>
```

</eslint-code-block>

### `"type": "line"`

<eslint-code-block :rules="{'vue/require-prop-comment': ['error', {type: 'line'}]}">

```vue
<script setup>
// ✓ GOOD
const goodProps = defineProps({
// line comment
a: Number,
})
// ✗ BAD
const badProps = defineProps({
/** JSDoc comment */
b: Number,
/* block comment */
c: Number,
d: Number,
})
</script>
```

</eslint-code-block>

### `"type": "any"`

<eslint-code-block :rules="{'vue/require-prop-comment': ['error', {type: 'any'}]}">

```vue
<script setup>
// ✓ GOOD
const goodProps = defineProps({
/** JSDoc comment */
a: Number,
/* block comment */
b: Number,
// line comment
c: Number,
})
// ✗ BAD
const badProps = defineProps({
d: Number,
})
</script>
```

</eslint-code-block>

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-prop-comment.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-prop-comment.js)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -176,6 +176,7 @@ module.exports = {
'require-explicit-emits': require('./rules/require-explicit-emits'),
'require-expose': require('./rules/require-expose'),
'require-name-property': require('./rules/require-name-property'),
'require-prop-comment': require('./rules/require-prop-comment'),
'require-prop-type-constructor': require('./rules/require-prop-type-constructor'),
'require-prop-types': require('./rules/require-prop-types'),
'require-render-return': require('./rules/require-render-return'),
Expand Down
123 changes: 123 additions & 0 deletions lib/rules/require-prop-comment.js
@@ -0,0 +1,123 @@
/**
* @author CZB
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../utils')

module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'require props to have a comment',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/require-prop-comment.html'
},
fixable: null,
schema: [
{
type: 'object',
properties: {
type: { enum: ['JSDoc', 'line', 'block', 'any'] }
},
additionalProperties: false
}
],
messages: {
requireAnyComment: 'The "{{name}}" property should have a comment.',
requireLineComment: 'The "{{name}}" property should have a line comment.',
requireBlockComment:
'The "{{name}}" property should have a block comment.',
requireJSDocComment:
'The "{{name}}" property should have a JSDoc comment.'
}
},
/** @param {RuleContext} context */
create(context) {
/** @type {{type?: "JSDoc" | "line" | "block" | "any"}|undefined} */
const schema = context.options[0]
const type = (schema && schema.type) || 'JSDoc'

const sourceCode = context.getSourceCode()

/** @param {Comment | undefined} comment */
const verifyBlock = (comment) =>
comment && comment.type === 'Block' && comment.value.charAt(0) !== '*'
? undefined
: 'requireBlockComment'

/** @param {Comment | undefined} comment */
const verifyLine = (comment) =>
comment && comment.type === 'Line' ? undefined : 'requireLineComment'

/** @param {Comment | undefined} comment */
const verifyAny = (comment) => (comment ? undefined : 'requireAnyComment')

/** @param {Comment | undefined} comment */
const verifyJSDoc = (comment) =>
comment && comment.type === 'Block' && comment.value.charAt(0) === '*'
? undefined
: 'requireJSDocComment'

/**
* @param {import('../utils').ComponentProp[]} props
*/
function verifyProps(props) {
for (const prop of props) {
if (!prop.propName) {
continue
}

const precedingComments = sourceCode.getCommentsBefore(prop.node)
const lastPrecedingComment =
precedingComments.length > 0
? precedingComments[precedingComments.length - 1]
: undefined

/** @type {string|undefined} */
let messageId

switch (type) {
case 'block':
messageId = verifyBlock(lastPrecedingComment)
break
case 'line':
messageId = verifyLine(lastPrecedingComment)
break
case 'any':
messageId = verifyAny(lastPrecedingComment)
break
default:
messageId = verifyJSDoc(lastPrecedingComment)
break
}

if (!messageId) {
continue
}

context.report({
node: prop.node,
messageId,
data: {
name: prop.propName
}
})
}
}

return utils.compositingVisitors(
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(_node, props) {
verifyProps(props)
}
}),
utils.defineVueVisitor(context, {
onVueObjectEnter(node) {
verifyProps(utils.getComponentPropsFromOptions(node))
}
})
)
}
}

0 comments on commit bf5829c

Please sign in to comment.