Skip to content

Commit 176aa8b

Browse files
authored
Fix #616 - update max attrs per line error message, remove white spaces (#643)
* Fix #616 - Improve error message in max-attributes-per-line, remove white spaces * Update .eslintrc - allow report message value to start with curly brace * 616 - Add failing cases * 616 - Improve events and other directives reporting * 616 Handle v-bind case
1 parent dcaccd5 commit 176aa8b

File tree

4 files changed

+105
-19
lines changed

4 files changed

+105
-19
lines changed

Diff for: .eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module.exports = {
1919
'eslint-plugin'
2020
],
2121
rules: {
22-
'eslint-plugin/report-message-format': ['error', '^[A-Z`\'].*\\.$'],
22+
'eslint-plugin/report-message-format': ['error', '^[A-Z`\'{].*\\.$'],
2323
'eslint-plugin/prefer-placeholders': 'error',
2424
'eslint-plugin/consistent-output': 'error'
2525
},

Diff for: lib/rules/max-attributes-per-line.js

+39-9
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ module.exports = {
7070
const multilineMaximum = configuration.multiline
7171
const singlelinemMaximum = configuration.singleline
7272
const canHaveFirstLine = configuration.allowFirstLine
73+
const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
7374

7475
return utils.defineTemplateBodyVisitor(context, {
7576
'VStartTag' (node) {
@@ -78,17 +79,17 @@ module.exports = {
7879
if (!numberOfAttributes) return
7980

8081
if (utils.isSingleLine(node) && numberOfAttributes > singlelinemMaximum) {
81-
showErrors(node.attributes.slice(singlelinemMaximum), node)
82+
showErrors(node.attributes.slice(singlelinemMaximum))
8283
}
8384

8485
if (!utils.isSingleLine(node)) {
8586
if (!canHaveFirstLine && node.attributes[0].loc.start.line === node.loc.start.line) {
86-
showErrors([node.attributes[0]], node)
87+
showErrors([node.attributes[0]])
8788
}
8889

8990
groupAttrsByLine(node.attributes)
9091
.filter(attrs => attrs.length > multilineMaximum)
91-
.forEach(attrs => showErrors(attrs.splice(multilineMaximum), node))
92+
.forEach(attrs => showErrors(attrs.splice(multilineMaximum)))
9293
}
9394
}
9495
})
@@ -128,16 +129,45 @@ module.exports = {
128129
return defaults
129130
}
130131

131-
function showErrors (attributes, node) {
132+
function getPropData (prop) {
133+
let propType = 'Attribute'
134+
let propName = prop.key.name
135+
136+
if (utils.isBindingAttribute(prop)) {
137+
propType = 'Binding'
138+
propName = prop.key.raw.argument
139+
} else if (utils.isEventAttribute(prop)) {
140+
propType = 'Event'
141+
propName = prop.key.raw.argument
142+
} else if (prop.directive) {
143+
propType = 'Directive'
144+
}
145+
146+
return { propType, propName }
147+
}
148+
149+
function showErrors (attributes) {
132150
attributes.forEach((prop, i) => {
151+
const fix = (fixer) => {
152+
if (i !== 0) return null
153+
154+
// Find the closest token before the current prop
155+
// that is not a white space
156+
const prevToken = template.getTokenBefore(prop, {
157+
filter: (token) => token.type !== 'HTMLWhitespace'
158+
})
159+
160+
const range = [prevToken.range[1], prop.range[0]]
161+
162+
return fixer.replaceTextRange(range, '\n')
163+
}
164+
133165
context.report({
134166
node: prop,
135167
loc: prop.loc,
136-
message: 'Attribute "{{propName}}" should be on a new line.',
137-
data: {
138-
propName: prop.key.name
139-
},
140-
fix: i === 0 ? (fixer) => fixer.insertTextBefore(prop, '\n') : undefined
168+
message: '{{propType}} "{{propName}}" should be on a new line.',
169+
data: getPropData(prop),
170+
fix
141171
})
142172
})
143173
}

Diff for: lib/utils/index.js

+20
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,26 @@ module.exports = {
294294
return VOID_ELEMENT_NAMES.has(name)
295295
},
296296

297+
/**
298+
* Check whether the given attribute node is a binding
299+
* @param {ASTNode} name The attribute to check.
300+
* @returns {boolean}
301+
*/
302+
isBindingAttribute (attribute) {
303+
return attribute.directive &&
304+
attribute.key.name === 'bind' &&
305+
attribute.key.argument
306+
},
307+
308+
/**
309+
* Check whether the given attribute node is an event
310+
* @param {ASTNode} name The attribute to check.
311+
* @returns {boolean}
312+
*/
313+
isEventAttribute (attribute) {
314+
return attribute.directive && attribute.key.name === 'on'
315+
},
316+
297317
/**
298318
* Parse member expression node to get array with all of its parts
299319
* @param {ASTNode} MemberExpression

Diff for: tests/lib/rules/max-attributes-per-line.js

+45-9
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,53 @@ ruleTester.run('max-attributes-per-line', rule, {
9191
invalid: [
9292
{
9393
code: `<template><component name="John Doe" age="30"></component></template>`,
94-
output: `<template><component name="John Doe"
94+
output: `<template><component name="John Doe"
9595
age="30"></component></template>`,
9696
errors: ['Attribute "age" should be on a new line.']
9797
},
98+
{
99+
code: `<template><component :name="user.name" :age="user.age"></component></template>`,
100+
output: `<template><component :name="user.name"
101+
:age="user.age"></component></template>`,
102+
errors: ['Binding "age" should be on a new line.']
103+
},
104+
{
105+
code: `<template><component :is="test" v-bind="user"></component></template>`,
106+
output: `<template><component :is="test"
107+
v-bind="user"></component></template>`,
108+
errors: ['Directive "bind" should be on a new line.']
109+
},
110+
{
111+
code: `<template><component :name="user.name" @buy="buyProduct"></component></template>`,
112+
output: `<template><component :name="user.name"
113+
@buy="buyProduct"></component></template>`,
114+
errors: ['Event "buy" should be on a new line.']
115+
},
116+
{
117+
code: `<template><component :name="user.name" @click.stop></component></template>`,
118+
output: `<template><component :name="user.name"
119+
@click.stop></component></template>`,
120+
errors: ['Event "click" should be on a new line.']
121+
},
122+
{
123+
code: `<template><component :name="user.name" v-if="something"></component></template>`,
124+
output: `<template><component :name="user.name"
125+
v-if="something"></component></template>`,
126+
errors: ['Directive "if" should be on a new line.']
127+
},
128+
{
129+
code: `<template><component name="John Doe" v-bind:age="user.age"></component></template>`,
130+
output: `<template><component name="John Doe"
131+
v-bind:age="user.age"></component></template>`,
132+
errors: ['Binding "age" should be on a new line.']
133+
},
98134
{
99135
code: `<template><component job="Vet"
100136
name="John Doe"
101137
age="30">
102138
</component>
103139
</template>`,
104-
output: `<template><component
140+
output: `<template><component
105141
job="Vet"
106142
name="John Doe"
107143
age="30">
@@ -116,7 +152,7 @@ job="Vet"
116152
{
117153
code: `<template><component name="John Doe" age="30" job="Vet"></component></template>`,
118154
options: [{ singleline: { max: 2 }}],
119-
output: `<template><component name="John Doe" age="30"
155+
output: `<template><component name="John Doe" age="30"
120156
job="Vet"></component></template>`,
121157
errors: [{
122158
message: 'Attribute "job" should be on a new line.',
@@ -127,7 +163,7 @@ job="Vet"></component></template>`,
127163
{
128164
code: `<template><component name="John Doe" age="30" job="Vet"></component></template>`,
129165
options: [{ singleline: 1, multiline: { max: 1, allowFirstLine: false }}],
130-
output: `<template><component name="John Doe"
166+
output: `<template><component name="John Doe"
131167
age="30" job="Vet"></component></template>`,
132168
errors: [{
133169
message: 'Attribute "age" should be on a new line.',
@@ -145,7 +181,7 @@ age="30" job="Vet"></component></template>`,
145181
</component>
146182
</template>`,
147183
options: [{ singleline: 3, multiline: { max: 1, allowFirstLine: false }}],
148-
output: `<template><component
184+
output: `<template><component
149185
name="John Doe"
150186
age="30">
151187
</component>
@@ -164,7 +200,7 @@ name="John Doe"
164200
</template>`,
165201
options: [{ singleline: 3, multiline: { max: 1, allowFirstLine: false }}],
166202
output: `<template><component
167-
name="John Doe"
203+
name="John Doe"
168204
age="30"
169205
job="Vet">
170206
</component>
@@ -183,7 +219,7 @@ age="30"
183219
</template>`,
184220
options: [{ singleline: 3, multiline: 1 }],
185221
output: `<template><component
186-
name="John Doe"
222+
name="John Doe"
187223
age="30"
188224
job="Vet">
189225
</component>
@@ -203,7 +239,7 @@ age="30"
203239
options: [{ singleline: 3, multiline: { max: 2, allowFirstLine: false }}],
204240
output: `<template><component
205241
name="John Doe" age="30"
206-
job="Vet" pet="dog"
242+
job="Vet" pet="dog"
207243
petname="Snoopy">
208244
</component>
209245
</template>`,
@@ -222,7 +258,7 @@ petname="Snoopy">
222258
options: [{ singleline: 3, multiline: { max: 2, allowFirstLine: false }}],
223259
output: `<template><component
224260
name="John Doe" age="30"
225-
job="Vet" pet="dog"
261+
job="Vet" pet="dog"
226262
petname="Snoopy" extra="foo">
227263
</component>
228264
</template>`,

0 commit comments

Comments
 (0)