Skip to content

Commit cc1b8d9

Browse files
small-tousunxinyu
and
sunxinyu
authored
feat: rule fixer for require-name-property (#2034)
Co-authored-by: sunxinyu <[email protected]>
1 parent 9673a61 commit cc1b8d9

File tree

4 files changed

+207
-6
lines changed

4 files changed

+207
-6
lines changed

docs/rules/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ For example:
259259
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | :hammer: |
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: |
262-
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | | :hammer: |
262+
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | :bulb: | :hammer: |
263263
| [vue/require-prop-comment](./require-prop-comment.md) | require props to have a comment | | :hammer: |
264264
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: | :lipstick: |
265265
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | | :hammer: |

docs/rules/require-name-property.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ since: v6.1.0
99

1010
> require a name property in Vue components
1111
12+
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
13+
1214
## :book: Rule Details
1315

1416
This rule requires a `name` property to be set on components.

lib/rules/require-name-property.js

+58-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,31 @@
44
*/
55
'use strict'
66

7+
const path = require('path')
78
const utils = require('../utils')
89
const { getVueComponentDefinitionType } = require('../utils')
10+
/**
11+
* Get the text of the empty indentation part of the line which the given token is on.
12+
* @param {SourceCode} sourceCode the source code object
13+
* @param {Token} token the token to get the indentation text of the line which the token is on
14+
* @returns {string} The text of indentation part.
15+
*/
16+
function getLineEmptyIndent(sourceCode, token) {
17+
const LT_CHAR = /[\n\r\u2028\u2029]/
18+
const EMPTY_CHAR = /\s/
19+
const text = sourceCode.text
20+
let i = token.range[0] - 1
921

22+
while (i >= 0 && !LT_CHAR.test(text[i])) {
23+
i -= 1
24+
}
25+
let j = i
26+
while (EMPTY_CHAR.test(text[j])) {
27+
j += 1
28+
}
29+
30+
return text.slice(i + 1, j)
31+
}
1032
/**
1133
* @param {Property | SpreadElement} node
1234
* @returns {node is ObjectExpressionProperty}
@@ -28,6 +50,7 @@ module.exports = {
2850
url: 'https://eslint.vuejs.org/rules/require-name-property.html'
2951
},
3052
fixable: null,
53+
hasSuggestions: true,
3154
schema: []
3255
},
3356
/** @param {RuleContext} context */
@@ -47,7 +70,41 @@ module.exports = {
4770

4871
context.report({
4972
node: component,
50-
message: 'Required name property is not set.'
73+
message: 'Required name property is not set.',
74+
suggest: [
75+
{
76+
desc: 'Add name property to component.',
77+
fix(fixer) {
78+
const extension = path.extname(context.getFilename())
79+
const filename = path.basename(context.getFilename(), extension)
80+
const sourceCode = context.getSourceCode()
81+
// fix only when property is not empty
82+
if (component.properties.length > 0) {
83+
const firstToken = sourceCode.getFirstToken(
84+
component.properties[0]
85+
)
86+
const indentText = getLineEmptyIndent(sourceCode, firstToken)
87+
// insert name property before the first property
88+
return fixer.insertTextBefore(
89+
component.properties[0],
90+
`name: '${filename}',\n${indentText}`
91+
)
92+
}
93+
94+
const firstToken = sourceCode.getFirstToken(component)
95+
const lastToken = sourceCode.getLastToken(component)
96+
// if the component is empty, insert name property and indent
97+
if (firstToken.value === '{' && lastToken.value === '}') {
98+
const indentText = getLineEmptyIndent(sourceCode, firstToken)
99+
return fixer.replaceTextRange(
100+
[firstToken.range[1], lastToken.range[0]],
101+
`\n${indentText} name: '${filename}'\n${indentText}`
102+
)
103+
}
104+
return null
105+
}
106+
}
107+
]
51108
})
52109
})
53110
}

tests/lib/rules/require-name-property.js

+146-4
Original file line numberDiff line numberDiff line change
@@ -78,25 +78,143 @@ ruleTester.run('require-name-property', rule, {
7878
errors: [
7979
{
8080
message: 'Required name property is not set.',
81-
type: 'ObjectExpression'
81+
type: 'ObjectExpression',
82+
suggestions: [
83+
{
84+
desc: 'Add name property to component.',
85+
output: `
86+
export default {
87+
name: 'InvalidComponent'
88+
}
89+
`
90+
}
91+
]
8292
}
8393
]
8494
},
8595
{
8696
filename: 'InvalidComponent.vue',
8797
code: `
98+
export default defineComponent({
99+
})
100+
`,
101+
parserOptions,
102+
errors: [
103+
{
104+
message: 'Required name property is not set.',
105+
type: 'ObjectExpression',
106+
suggestions: [
107+
{
108+
desc: 'Add name property to component.',
109+
output: `
110+
export default defineComponent({
111+
name: 'InvalidComponent'
112+
})
113+
`
114+
}
115+
]
116+
}
117+
]
118+
},
119+
{
120+
filename: 'InvalidComponent.vue',
121+
code: `
122+
export default defineComponent({ })
123+
`,
124+
parserOptions,
125+
errors: [
126+
{
127+
message: 'Required name property is not set.',
128+
type: 'ObjectExpression',
129+
suggestions: [
130+
{
131+
desc: 'Add name property to component.',
132+
output: `
133+
export default defineComponent({
134+
name: 'InvalidComponent'
135+
})
136+
`
137+
}
138+
]
139+
}
140+
]
141+
},
142+
{
143+
filename: 'InvalidComponent.vue',
144+
code: `
145+
export default { }
146+
`,
147+
parserOptions,
148+
errors: [
149+
{
150+
message: 'Required name property is not set.',
151+
type: 'ObjectExpression',
152+
suggestions: [
153+
{
154+
desc: 'Add name property to component.',
155+
output: `
88156
export default {
157+
name: 'InvalidComponent'
158+
}
159+
`
160+
}
161+
]
162+
}
163+
]
164+
},
165+
{
166+
filename: 'InvalidComponent.vue',
167+
code: `
168+
export default {
169+
nameNot: 'IssaNameNot'
170+
}
171+
`,
172+
parserOptions,
173+
errors: [
174+
{
175+
message: 'Required name property is not set.',
176+
type: 'ObjectExpression',
177+
suggestions: [
178+
{
179+
desc: 'Add name property to component.',
180+
output: `
181+
export default {
182+
name: 'InvalidComponent',
89183
nameNot: 'IssaNameNot'
90184
}
185+
`
186+
}
187+
]
188+
}
189+
]
190+
},
191+
{
192+
filename: 'InvalidComponent.vue',
193+
code: `
194+
export default defineComponent({
195+
nameNot: 'IssaNameNot'
196+
})
91197
`,
92198
parserOptions,
93199
errors: [
94200
{
95201
message: 'Required name property is not set.',
96-
type: 'ObjectExpression'
202+
type: 'ObjectExpression',
203+
suggestions: [
204+
{
205+
desc: 'Add name property to component.',
206+
output: `
207+
export default defineComponent({
208+
name: 'InvalidComponent',
209+
nameNot: 'IssaNameNot'
210+
})
211+
`
212+
}
213+
]
97214
}
98215
]
99216
},
217+
100218
{
101219
filename: 'InvalidComponent.vue',
102220
code: `
@@ -110,7 +228,20 @@ ruleTester.run('require-name-property', rule, {
110228
errors: [
111229
{
112230
message: 'Required name property is not set.',
113-
type: 'ObjectExpression'
231+
type: 'ObjectExpression',
232+
suggestions: [
233+
{
234+
desc: 'Add name property to component.',
235+
output: `
236+
export default {
237+
name: 'InvalidComponent',
238+
computed: {
239+
name() { return 'name' }
240+
}
241+
}
242+
`
243+
}
244+
]
114245
}
115246
]
116247
},
@@ -125,7 +256,18 @@ ruleTester.run('require-name-property', rule, {
125256
errors: [
126257
{
127258
message: 'Required name property is not set.',
128-
type: 'ObjectExpression'
259+
type: 'ObjectExpression',
260+
suggestions: [
261+
{
262+
desc: 'Add name property to component.',
263+
output: `
264+
export default {
265+
name: 'InvalidComponent',
266+
[name]: 'IssaName'
267+
}
268+
`
269+
}
270+
]
129271
}
130272
]
131273
}

0 commit comments

Comments
 (0)