Skip to content

Commit 4d79ceb

Browse files
dev1437FloEdelmann
andauthored
Create vue/multiline-ternary extension rule (#1996)
* Create initial multiline ternary rule * Docs * Linting * Tests and apply Document * Fix import * Fix tests * Fix test * Run npm run update * Update tests * lint * Update doc * Add tests for script tag * Update tests/lib/rules/multiline-ternary.js Co-authored-by: Flo Edelmann <[email protected]> * Add example * Lint Co-authored-by: Flo Edelmann <[email protected]>
1 parent db3a1c1 commit 4d79ceb

File tree

6 files changed

+376
-0
lines changed

6 files changed

+376
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
292292
| [vue/key-spacing](./key-spacing.md) | Enforce consistent spacing between keys and values in object literal properties in `<template>` | :wrench: | :lipstick: |
293293
| [vue/keyword-spacing](./keyword-spacing.md) | Enforce consistent spacing before and after keywords in `<template>` | :wrench: | :lipstick: |
294294
| [vue/max-len](./max-len.md) | enforce a maximum line length in `.vue` files | | :lipstick: |
295+
| [vue/multiline-ternary](./multiline-ternary.md) | Enforce newlines between operands of ternary expressions in `<template>` | :wrench: | :lipstick: |
295296
| [vue/no-constant-condition](./no-constant-condition.md) | Disallow constant expressions in conditions in `<template>` | | :warning: |
296297
| [vue/no-empty-pattern](./no-empty-pattern.md) | Disallow empty destructuring patterns in `<template>` | | :warning: |
297298
| [vue/no-extra-parens](./no-extra-parens.md) | Disallow unnecessary parentheses in `<template>` | :wrench: | :lipstick: |

docs/rules/multiline-ternary.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/multiline-ternary
5+
description: Enforce newlines between operands of ternary expressions in `<template>`
6+
---
7+
# vue/multiline-ternary
8+
9+
> Enforce newlines between operands of ternary expressions in `<template>`
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+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
13+
14+
This rule is the same rule as core [multiline-ternary] rule but it applies to the expressions in `<template>` and `<style>`.
15+
16+
## :book: Rule Details
17+
18+
<eslint-code-block fix :rules="{'vue/multiline-ternary': ['error']}">
19+
20+
```vue
21+
<template>
22+
<div>
23+
<!-- ✓ GOOD -->
24+
<div :class="isEnabled
25+
? 'check'
26+
: 'stop'" />
27+
28+
<!-- ✗ BAD -->
29+
<div :class="isEnabled ? 'check' : 'stop'" />
30+
</div>
31+
</template>
32+
33+
<style>
34+
div {
35+
/* ✓ GOOD */
36+
color: v-bind('myFlag
37+
? foo
38+
: bar');
39+
40+
/* ✗ BAD */
41+
color: v-bind('myFlag ? foo : bar');
42+
}
43+
</style>
44+
```
45+
46+
</eslint-code-block>
47+
48+
## :books: Further Reading
49+
50+
- [multiline-ternary]
51+
52+
[multiline-ternary]: https://eslint.org/docs/rules/multiline-ternary
53+
54+
## :mag: Implementation
55+
56+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/multiline-ternary.js)
57+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/multiline-ternary.js)
58+
59+
<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/multiline-ternary)</sup>

lib/configs/no-layout-rules.js

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module.exports = {
3131
'vue/max-attributes-per-line': 'off',
3232
'vue/max-len': 'off',
3333
'vue/multiline-html-element-content-newline': 'off',
34+
'vue/multiline-ternary': 'off',
3435
'vue/mustache-interpolation-spacing': 'off',
3536
'vue/new-line-between-multi-line-property': 'off',
3637
'vue/no-extra-parens': 'off',

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ module.exports = {
5454
'max-len': require('./rules/max-len'),
5555
'multi-word-component-names': require('./rules/multi-word-component-names'),
5656
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
57+
'multiline-ternary': require('./rules/multiline-ternary'),
5758
'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'),
5859
'new-line-between-multi-line-property': require('./rules/new-line-between-multi-line-property'),
5960
'next-tick-style': require('./rules/next-tick-style'),

lib/rules/multiline-ternary.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @author dev1437
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const { wrapCoreRule } = require('../utils')
12+
13+
// eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
14+
module.exports = wrapCoreRule('multiline-ternary', {
15+
skipDynamicArguments: true,
16+
applyDocument: true
17+
})

tests/lib/rules/multiline-ternary.js

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/**
2+
* @author dev1437
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const { RuleTester, ESLint } = require('../../eslint-compat')
8+
const rule = require('../../../lib/rules/multiline-ternary')
9+
const semver = require('semver')
10+
11+
const tester = new RuleTester({
12+
parser: require.resolve('vue-eslint-parser'),
13+
parserOptions: {
14+
ecmaVersion: 2020,
15+
sourceType: 'module'
16+
}
17+
})
18+
19+
tester.run('multiline-ternary', rule, {
20+
valid: [
21+
{
22+
filename: 'test.vue',
23+
code: `
24+
<template>
25+
<div :class="{
26+
'test': someReallyLongCondition ?
27+
aVeryLongOutput :
28+
thisCantFitOnASingleLine
29+
}">
30+
</div>
31+
</template>
32+
`
33+
},
34+
{
35+
// doesn't check ternary statements in <script> block
36+
filename: 'test.vue',
37+
code: `
38+
<script>
39+
let test = someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
40+
</script>
41+
`
42+
},
43+
{
44+
filename: 'test.vue',
45+
code: `
46+
<template>
47+
<div :class="{
48+
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
49+
}">
50+
</div>
51+
</template>
52+
`,
53+
options: ['never']
54+
},
55+
{
56+
filename: 'test.vue',
57+
code: `
58+
<template>
59+
<div class="test">
60+
</div>
61+
</template>
62+
<style>
63+
.test {
64+
color: v-bind('someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine')
65+
}
66+
</style>
67+
`,
68+
options: ['never']
69+
}
70+
],
71+
invalid: [
72+
{
73+
filename: 'test.vue',
74+
code: `
75+
<template>
76+
<div :class="{
77+
'test': someReallyLongCondition ?
78+
aVeryLongOutput : thisCantFitOnASingleLine
79+
}">
80+
</div>
81+
</template>
82+
`,
83+
output: semver.gte(ESLint.version, '7.1.0')
84+
? `
85+
<template>
86+
<div :class="{
87+
'test': someReallyLongCondition ?
88+
aVeryLongOutput
89+
: thisCantFitOnASingleLine
90+
}">
91+
</div>
92+
</template>
93+
`
94+
: null,
95+
errors: [
96+
{
97+
message:
98+
'Expected newline between consequent and alternate of ternary expression.',
99+
line: 5,
100+
column: 15
101+
}
102+
],
103+
options: ['always-multiline']
104+
},
105+
{
106+
filename: 'test.vue',
107+
code: `
108+
<template>
109+
<div :class="{
110+
'test': someReallyLongCondition ?
111+
aVeryLongOutput : thisCantFitOnASingleLine
112+
}">
113+
</div>
114+
</template>
115+
`,
116+
output: semver.gte(ESLint.version, '7.1.0')
117+
? `
118+
<template>
119+
<div :class="{
120+
'test': someReallyLongCondition ?aVeryLongOutput : thisCantFitOnASingleLine
121+
}">
122+
</div>
123+
</template>
124+
`
125+
: null,
126+
errors: [
127+
{
128+
message:
129+
'Unexpected newline between test and consequent of ternary expression.',
130+
line: 4,
131+
column: 21
132+
}
133+
],
134+
options: ['never']
135+
},
136+
{
137+
filename: 'test.vue',
138+
code: `
139+
<template>
140+
<div :class="{
141+
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
142+
}">
143+
</div>
144+
</template>
145+
`,
146+
output: semver.gte(ESLint.version, '7.1.0')
147+
? `
148+
<template>
149+
<div :class="{
150+
'test': someReallyLongCondition
151+
? aVeryLongOutput
152+
: thisCantFitOnASingleLine
153+
}">
154+
</div>
155+
</template>
156+
`
157+
: null,
158+
errors: [
159+
{
160+
message:
161+
'Expected newline between test and consequent of ternary expression.',
162+
line: 4,
163+
column: 21
164+
},
165+
{
166+
message:
167+
'Expected newline between consequent and alternate of ternary expression.',
168+
line: 4,
169+
column: 47
170+
}
171+
]
172+
},
173+
{
174+
filename: 'test.vue',
175+
code: `
176+
<template>
177+
<div :style="{
178+
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
179+
}">
180+
</div>
181+
</template>
182+
`,
183+
output: semver.gte(ESLint.version, '7.1.0')
184+
? `
185+
<template>
186+
<div :style="{
187+
'test': someReallyLongCondition
188+
? aVeryLongOutput
189+
: thisCantFitOnASingleLine
190+
}">
191+
</div>
192+
</template>
193+
`
194+
: null,
195+
errors: [
196+
{
197+
message:
198+
'Expected newline between test and consequent of ternary expression.',
199+
line: 4,
200+
column: 21
201+
},
202+
{
203+
message:
204+
'Expected newline between consequent and alternate of ternary expression.',
205+
line: 4,
206+
column: 47
207+
}
208+
]
209+
},
210+
{
211+
filename: 'test.vue',
212+
code: `
213+
<template>
214+
<div class="test">
215+
</div>
216+
</template>
217+
<style>
218+
.test {
219+
color: v-bind('someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine')
220+
}
221+
</style>
222+
`,
223+
output: semver.gte(ESLint.version, '7.1.0')
224+
? `
225+
<template>
226+
<div class="test">
227+
</div>
228+
</template>
229+
<style>
230+
.test {
231+
color: v-bind('someReallyLongCondition
232+
? aVeryLongOutput
233+
: thisCantFitOnASingleLine')
234+
}
235+
</style>
236+
`
237+
: null,
238+
errors: [
239+
{
240+
message:
241+
'Expected newline between test and consequent of ternary expression.',
242+
line: 8,
243+
column: 30
244+
},
245+
{
246+
message:
247+
'Expected newline between consequent and alternate of ternary expression.',
248+
line: 8,
249+
column: 56
250+
}
251+
]
252+
},
253+
{
254+
filename: 'test.vue',
255+
code: `
256+
<template>
257+
<div :class="{
258+
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
259+
}">
260+
</div>
261+
</template>
262+
<script>
263+
let test = someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
264+
</script>
265+
`,
266+
output: semver.gte(ESLint.version, '7.1.0')
267+
? `
268+
<template>
269+
<div :class="{
270+
'test': someReallyLongCondition
271+
? aVeryLongOutput
272+
: thisCantFitOnASingleLine
273+
}">
274+
</div>
275+
</template>
276+
<script>
277+
let test = someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
278+
</script>
279+
`
280+
: null,
281+
errors: [
282+
{
283+
message:
284+
'Expected newline between test and consequent of ternary expression.',
285+
line: 4,
286+
column: 19
287+
},
288+
{
289+
message:
290+
'Expected newline between consequent and alternate of ternary expression.',
291+
line: 4,
292+
column: 45
293+
}
294+
]
295+
}
296+
]
297+
})

0 commit comments

Comments
 (0)