Skip to content

Commit

Permalink
Create vue/multiline-ternary extension rule (#1996)
Browse files Browse the repository at this point in the history
* 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 <florian-edelmann@online.de>

* Add example

* Lint

Co-authored-by: Flo Edelmann <florian-edelmann@online.de>
  • Loading branch information
dev1437 and FloEdelmann committed Oct 10, 2022
1 parent db3a1c1 commit 4d79ceb
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -292,6 +292,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
| [vue/key-spacing](./key-spacing.md) | Enforce consistent spacing between keys and values in object literal properties in `<template>` | :wrench: | :lipstick: |
| [vue/keyword-spacing](./keyword-spacing.md) | Enforce consistent spacing before and after keywords in `<template>` | :wrench: | :lipstick: |
| [vue/max-len](./max-len.md) | enforce a maximum line length in `.vue` files | | :lipstick: |
| [vue/multiline-ternary](./multiline-ternary.md) | Enforce newlines between operands of ternary expressions in `<template>` | :wrench: | :lipstick: |
| [vue/no-constant-condition](./no-constant-condition.md) | Disallow constant expressions in conditions in `<template>` | | :warning: |
| [vue/no-empty-pattern](./no-empty-pattern.md) | Disallow empty destructuring patterns in `<template>` | | :warning: |
| [vue/no-extra-parens](./no-extra-parens.md) | Disallow unnecessary parentheses in `<template>` | :wrench: | :lipstick: |
Expand Down
59 changes: 59 additions & 0 deletions docs/rules/multiline-ternary.md
@@ -0,0 +1,59 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/multiline-ternary
description: Enforce newlines between operands of ternary expressions in `<template>`
---
# vue/multiline-ternary

> Enforce newlines between operands of ternary expressions in `<template>`
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
- :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.

This rule is the same rule as core [multiline-ternary] rule but it applies to the expressions in `<template>` and `<style>`.

## :book: Rule Details

<eslint-code-block fix :rules="{'vue/multiline-ternary': ['error']}">

```vue
<template>
<div>
<!-- ✓ GOOD -->
<div :class="isEnabled
? 'check'
: 'stop'" />
<!-- ✗ BAD -->
<div :class="isEnabled ? 'check' : 'stop'" />
</div>
</template>
<style>
div {
/* ✓ GOOD */
color: v-bind('myFlag
? foo
: bar');
/* ✗ BAD */
color: v-bind('myFlag ? foo : bar');
}
</style>
```

</eslint-code-block>

## :books: Further Reading

- [multiline-ternary]

[multiline-ternary]: https://eslint.org/docs/rules/multiline-ternary

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/multiline-ternary.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/multiline-ternary.js)

<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/multiline-ternary)</sup>
1 change: 1 addition & 0 deletions lib/configs/no-layout-rules.js
Expand Up @@ -31,6 +31,7 @@ module.exports = {
'vue/max-attributes-per-line': 'off',
'vue/max-len': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/multiline-ternary': 'off',
'vue/mustache-interpolation-spacing': 'off',
'vue/new-line-between-multi-line-property': 'off',
'vue/no-extra-parens': 'off',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -54,6 +54,7 @@ module.exports = {
'max-len': require('./rules/max-len'),
'multi-word-component-names': require('./rules/multi-word-component-names'),
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
'multiline-ternary': require('./rules/multiline-ternary'),
'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'),
'new-line-between-multi-line-property': require('./rules/new-line-between-multi-line-property'),
'next-tick-style': require('./rules/next-tick-style'),
Expand Down
17 changes: 17 additions & 0 deletions lib/rules/multiline-ternary.js
@@ -0,0 +1,17 @@
/**
* @author dev1437
* See LICENSE file in root directory for full license.
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
module.exports = wrapCoreRule('multiline-ternary', {
skipDynamicArguments: true,
applyDocument: true
})
297 changes: 297 additions & 0 deletions tests/lib/rules/multiline-ternary.js
@@ -0,0 +1,297 @@
/**
* @author dev1437
* See LICENSE file in root directory for full license.
*/
'use strict'

const { RuleTester, ESLint } = require('../../eslint-compat')
const rule = require('../../../lib/rules/multiline-ternary')
const semver = require('semver')

const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
}
})

tester.run('multiline-ternary', rule, {
valid: [
{
filename: 'test.vue',
code: `
<template>
<div :class="{
'test': someReallyLongCondition ?
aVeryLongOutput :
thisCantFitOnASingleLine
}">
</div>
</template>
`
},
{
// doesn't check ternary statements in <script> block
filename: 'test.vue',
code: `
<script>
let test = someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
</script>
`
},
{
filename: 'test.vue',
code: `
<template>
<div :class="{
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
`,
options: ['never']
},
{
filename: 'test.vue',
code: `
<template>
<div class="test">
</div>
</template>
<style>
.test {
color: v-bind('someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine')
}
</style>
`,
options: ['never']
}
],
invalid: [
{
filename: 'test.vue',
code: `
<template>
<div :class="{
'test': someReallyLongCondition ?
aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
`,
output: semver.gte(ESLint.version, '7.1.0')
? `
<template>
<div :class="{
'test': someReallyLongCondition ?
aVeryLongOutput
: thisCantFitOnASingleLine
}">
</div>
</template>
`
: null,
errors: [
{
message:
'Expected newline between consequent and alternate of ternary expression.',
line: 5,
column: 15
}
],
options: ['always-multiline']
},
{
filename: 'test.vue',
code: `
<template>
<div :class="{
'test': someReallyLongCondition ?
aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
`,
output: semver.gte(ESLint.version, '7.1.0')
? `
<template>
<div :class="{
'test': someReallyLongCondition ?aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
`
: null,
errors: [
{
message:
'Unexpected newline between test and consequent of ternary expression.',
line: 4,
column: 21
}
],
options: ['never']
},
{
filename: 'test.vue',
code: `
<template>
<div :class="{
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
`,
output: semver.gte(ESLint.version, '7.1.0')
? `
<template>
<div :class="{
'test': someReallyLongCondition
? aVeryLongOutput
: thisCantFitOnASingleLine
}">
</div>
</template>
`
: null,
errors: [
{
message:
'Expected newline between test and consequent of ternary expression.',
line: 4,
column: 21
},
{
message:
'Expected newline between consequent and alternate of ternary expression.',
line: 4,
column: 47
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div :style="{
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
`,
output: semver.gte(ESLint.version, '7.1.0')
? `
<template>
<div :style="{
'test': someReallyLongCondition
? aVeryLongOutput
: thisCantFitOnASingleLine
}">
</div>
</template>
`
: null,
errors: [
{
message:
'Expected newline between test and consequent of ternary expression.',
line: 4,
column: 21
},
{
message:
'Expected newline between consequent and alternate of ternary expression.',
line: 4,
column: 47
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div class="test">
</div>
</template>
<style>
.test {
color: v-bind('someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine')
}
</style>
`,
output: semver.gte(ESLint.version, '7.1.0')
? `
<template>
<div class="test">
</div>
</template>
<style>
.test {
color: v-bind('someReallyLongCondition
? aVeryLongOutput
: thisCantFitOnASingleLine')
}
</style>
`
: null,
errors: [
{
message:
'Expected newline between test and consequent of ternary expression.',
line: 8,
column: 30
},
{
message:
'Expected newline between consequent and alternate of ternary expression.',
line: 8,
column: 56
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div :class="{
'test': someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
}">
</div>
</template>
<script>
let test = someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
</script>
`,
output: semver.gte(ESLint.version, '7.1.0')
? `
<template>
<div :class="{
'test': someReallyLongCondition
? aVeryLongOutput
: thisCantFitOnASingleLine
}">
</div>
</template>
<script>
let test = someReallyLongCondition ? aVeryLongOutput : thisCantFitOnASingleLine
</script>
`
: null,
errors: [
{
message:
'Expected newline between test and consequent of ternary expression.',
line: 4,
column: 19
},
{
message:
'Expected newline between consequent and alternate of ternary expression.',
line: 4,
column: 45
}
]
}
]
})

0 comments on commit 4d79ceb

Please sign in to comment.