Skip to content

Commit

Permalink
feat: rule fixer for require-name-property
Browse files Browse the repository at this point in the history
  • Loading branch information
sunxinyu committed Nov 11, 2022
1 parent 9673a61 commit 5c5f742
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docs/rules/README.md
Expand Up @@ -259,7 +259,7 @@ For example:
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | :hammer: |
| [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-name-property](./require-name-property.md) | require a name property in Vue components | :bulb: | :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: |
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/require-name-property.md
Expand Up @@ -9,6 +9,8 @@ since: v6.1.0

> require a name property in Vue components
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).

## :book: Rule Details

This rule requires a `name` property to be set on components.
Expand Down
58 changes: 57 additions & 1 deletion lib/rules/require-name-property.js
Expand Up @@ -4,9 +4,31 @@
*/
'use strict'

const path = require('path')
const utils = require('../utils')
const { getVueComponentDefinitionType } = require('../utils')
/**
* Get the text of the empty indentation part of the line which the given token is on.
* @param {SourceCode} sourceCode the source code object
* @param {Token} token the token to get the indentation text of the line which the token is on
* @returns {string} The text of indentation part.
*/
function getLineEmptyIndent(sourceCode, token) {
const LT_CHAR = /[\n\r\u2028\u2029]/
const EMPTY_CHAR = /\s/
const text = sourceCode.text
let i = token.range[0] - 1

while (i >= 0 && !LT_CHAR.test(text[i])) {
i -= 1
}
let j = i
while (EMPTY_CHAR.test(text[j])) {
j += 1
}

return text.slice(i + 1, j)
}
/**
* @param {Property | SpreadElement} node
* @returns {node is ObjectExpressionProperty}
Expand All @@ -28,6 +50,7 @@ module.exports = {
url: 'https://eslint.vuejs.org/rules/require-name-property.html'
},
fixable: null,
hasSuggestions: true,
schema: []
},
/** @param {RuleContext} context */
Expand All @@ -47,7 +70,40 @@ module.exports = {

context.report({
node: component,
message: 'Required name property is not set.'
message: 'Required name property is not set.',
suggest: [
{
desc: 'Add name property to component.',
fix(fixer) {
const extension = path.extname(context.getFilename())
const filename = path.basename(context.getFilename(), extension)
// fix only when property is not empty
if (component.properties.length > 0) {
const startColumn = component.properties[0].loc.start.column
// insert name property before the first property
return fixer.insertTextBefore(
component.properties[0],
`name: '${filename}',\n${Array.from({
length: startColumn + 1
}).join(' ')}`
)
} else {
const sourceCode = context.getSourceCode()
const firstToken = sourceCode.getFirstToken(component)
const lastToken = sourceCode.getLastToken(component)
// if the component is empty, insert name property and indent
if (firstToken.value === '{' && lastToken.value === '}') {
const indentText = getLineEmptyIndent(sourceCode, firstToken)
return fixer.replaceTextRange(
[firstToken.range[1], lastToken.range[0]],
`\n${indentText} name: '${filename}'\n${indentText}`
)
}
}
return null
}
}
]
})
})
}
Expand Down
150 changes: 146 additions & 4 deletions tests/lib/rules/require-name-property.js
Expand Up @@ -78,25 +78,143 @@ ruleTester.run('require-name-property', rule, {
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression'
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default {
name: 'InvalidComponent'
}
`
}
]
}
]
},
{
filename: 'InvalidComponent.vue',
code: `
export default defineComponent({
})
`,
parserOptions,
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default defineComponent({
name: 'InvalidComponent'
})
`
}
]
}
]
},
{
filename: 'InvalidComponent.vue',
code: `
export default defineComponent({ })
`,
parserOptions,
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default defineComponent({
name: 'InvalidComponent'
})
`
}
]
}
]
},
{
filename: 'InvalidComponent.vue',
code: `
export default { }
`,
parserOptions,
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default {
name: 'InvalidComponent'
}
`
}
]
}
]
},
{
filename: 'InvalidComponent.vue',
code: `
export default {
nameNot: 'IssaNameNot'
}
`,
parserOptions,
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default {
name: 'InvalidComponent',
nameNot: 'IssaNameNot'
}
`
}
]
}
]
},
{
filename: 'InvalidComponent.vue',
code: `
export default defineComponent({
nameNot: 'IssaNameNot'
})
`,
parserOptions,
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression'
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default defineComponent({
name: 'InvalidComponent',
nameNot: 'IssaNameNot'
})
`
}
]
}
]
},

{
filename: 'InvalidComponent.vue',
code: `
Expand All @@ -110,7 +228,20 @@ ruleTester.run('require-name-property', rule, {
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression'
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default {
name: 'InvalidComponent',
computed: {
name() { return 'name' }
}
}
`
}
]
}
]
},
Expand All @@ -125,7 +256,18 @@ ruleTester.run('require-name-property', rule, {
errors: [
{
message: 'Required name property is not set.',
type: 'ObjectExpression'
type: 'ObjectExpression',
suggestions: [
{
desc: 'Add name property to component.',
output: `
export default {
name: 'InvalidComponent',
[name]: 'IssaName'
}
`
}
]
}
]
}
Expand Down

0 comments on commit 5c5f742

Please sign in to comment.