Skip to content

Commit 644f97d

Browse files
committed
feat: add vue/no-implicit-coercion rule
1 parent a2abbf0 commit 644f97d

File tree

5 files changed

+236
-0
lines changed

5 files changed

+236
-0
lines changed

Diff for: docs/rules/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
323323
| [vue/no-constant-condition](./no-constant-condition.md) | Disallow constant expressions in conditions in `<template>` | | :warning: |
324324
| [vue/no-empty-pattern](./no-empty-pattern.md) | Disallow empty destructuring patterns in `<template>` | | :warning: |
325325
| [vue/no-extra-parens](./no-extra-parens.md) | Disallow unnecessary parentheses in `<template>` | :wrench: | :lipstick: |
326+
| [vue/no-implicit-coercion](./no-implicit-coercion.md) | Disallow shorthand type conversions in `<template>` | :wrench: | :hammer: |
326327
| [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace in `.vue` files | | :warning: |
327328
| [vue/no-loss-of-precision](./no-loss-of-precision.md) | Disallow literal numbers that lose precision in `<template>` | | :warning: |
328329
| [vue/no-restricted-syntax](./no-restricted-syntax.md) | Disallow specified syntax in `<template>` | | :hammer: |

Diff for: docs/rules/no-implicit-coercion.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-implicit-coercion
5+
description: Disallow shorthand type conversions in `<template>`
6+
since: v9.33.0
7+
---
8+
9+
# vue/no-implicit-coercion
10+
11+
> Disallow shorthand type conversions in `<template>`
12+
13+
- :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.
14+
15+
This rule is the same rule as core [no-implicit-coercion] rule but it applies to the expressions in `<template>`.
16+
17+
## :books: Further Reading
18+
19+
- [no-implicit-coercion]
20+
21+
[no-implicit-coercion]: https://eslint.org/docs/rules/no-implicit-coercion
22+
23+
## :rocket: Version
24+
25+
This rule was introduced in eslint-plugin-vue v9.33.0
26+
27+
## :mag: Implementation
28+
29+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-implicit-coercion.js)
30+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-implicit-coercion.js)
31+
32+
<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-implicit-coercion)</sup>

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const plugin = {
126126
'no-export-in-script-setup': require('./rules/no-export-in-script-setup'),
127127
'no-expose-after-await': require('./rules/no-expose-after-await'),
128128
'no-extra-parens': require('./rules/no-extra-parens'),
129+
'no-implicit-coercion': require('./rules/no-implicit-coercion'),
129130
'no-invalid-model-keys': require('./rules/no-invalid-model-keys'),
130131
'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
131132
'no-lifecycle-after-await': require('./rules/no-lifecycle-after-await'),

Diff for: lib/rules/no-implicit-coercion.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @author lozinsky <https://github.com/lozinsky>
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
// eslint-disable-next-line internal/no-invalid-meta
10+
module.exports = utils.wrapCoreRule('no-implicit-coercion', {
11+
applyDocument: true
12+
})

Diff for: tests/lib/rules/no-implicit-coercion.js

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/**
2+
* @author lozinsky <https://github.com/lozinsky>
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('../../eslint-compat').RuleTester
8+
const rule = require('../../../lib/rules/no-implicit-coercion')
9+
10+
const tester = new RuleTester({
11+
languageOptions: {
12+
parser: require('vue-eslint-parser'),
13+
ecmaVersion: 2020,
14+
sourceType: 'module'
15+
}
16+
})
17+
18+
tester.run('no-implicit-coercion', rule, {
19+
valid: [
20+
`<template><div :data-foo="Boolean(foo)" /></template>`,
21+
`<template><div :data-foo="foo.indexOf('.') !== -1" /></template>`,
22+
{
23+
filename: 'test.vue',
24+
code: `<template><div :data-foo="!!foo" /></template>`,
25+
options: [
26+
{
27+
boolean: false
28+
}
29+
]
30+
},
31+
{
32+
filename: 'test.vue',
33+
code: `<template><div :data-foo="~foo.indexOf('.')" /></template>`,
34+
options: [
35+
{
36+
boolean: false
37+
}
38+
]
39+
},
40+
`<template><div :data-foo="Number(foo)" /></template>`,
41+
`<template><div :data-foo="foo * 1/4" /></template>`,
42+
{
43+
filename: 'test.vue',
44+
code: `<template><div :data-foo="+foo" /></template>`,
45+
options: [
46+
{
47+
number: false
48+
}
49+
]
50+
},
51+
{
52+
filename: 'test.vue',
53+
code: `<template><div :data-foo="1 * foo" /></template>`,
54+
options: [
55+
{
56+
number: false
57+
}
58+
]
59+
},
60+
`<template><div :data-foo="String(foo)" /></template>`,
61+
`<template><div :data-foo="\`\${foo}\`" /></template>`,
62+
{
63+
filename: 'test.vue',
64+
code: `<template><div :data-foo="'' + foo" /></template>`,
65+
options: [
66+
{
67+
string: false
68+
}
69+
]
70+
},
71+
{
72+
filename: 'test.vue',
73+
code: `<template><div :data-foo="\`\` + foo" /></template>`,
74+
options: [
75+
{
76+
string: false
77+
}
78+
]
79+
},
80+
{
81+
filename: 'test.vue',
82+
code: `<template><div :data-foo="!!foo" /></template>`,
83+
options: [
84+
{
85+
allow: ['!!']
86+
}
87+
]
88+
},
89+
{
90+
filename: 'test.vue',
91+
code: `<template><div :data-foo="~foo.indexOf('.')" /></template>`,
92+
options: [
93+
{
94+
allow: ['~']
95+
}
96+
]
97+
}
98+
],
99+
invalid: [
100+
{
101+
filename: 'test.vue',
102+
code: `<template><div :data-foo="!!foo" /></template>`,
103+
output: `<template><div :data-foo="Boolean(foo)" /></template>`,
104+
errors: [
105+
{
106+
message: 'use `Boolean(foo)` instead.',
107+
line: 1,
108+
column: 27
109+
}
110+
]
111+
},
112+
{
113+
filename: 'test.vue',
114+
code: `<template><div :data-foo="~foo.indexOf('.')" /></template>`,
115+
output: null,
116+
errors: [
117+
{
118+
message: "use `foo.indexOf('.') !== -1` instead.",
119+
line: 1,
120+
column: 27
121+
}
122+
]
123+
},
124+
{
125+
filename: 'test.vue',
126+
code: `<template><div :data-foo="+foo" /></template>`,
127+
output: `<template><div :data-foo="Number(foo)" /></template>`,
128+
errors: [
129+
{
130+
message: 'use `Number(foo)` instead.',
131+
line: 1,
132+
column: 27
133+
}
134+
]
135+
},
136+
{
137+
filename: 'test.vue',
138+
code: `<template><div :data-foo="1 * foo" /></template>`,
139+
output: `<template><div :data-foo="Number(foo)" /></template>`,
140+
errors: [
141+
{
142+
message: 'use `Number(foo)` instead.',
143+
line: 1,
144+
column: 27
145+
}
146+
]
147+
},
148+
{
149+
filename: 'test.vue',
150+
code: `<template><div :data-foo="'' + foo" /></template>`,
151+
output: `<template><div :data-foo="String(foo)" /></template>`,
152+
errors: [
153+
{
154+
message: 'use `String(foo)` instead.',
155+
line: 1,
156+
column: 27
157+
}
158+
]
159+
},
160+
{
161+
filename: 'test.vue',
162+
code: `<template><div :data-foo="\`\` + foo" /></template>`,
163+
output: `<template><div :data-foo="String(foo)" /></template>`,
164+
errors: [
165+
{
166+
message: 'use `String(foo)` instead.',
167+
line: 1,
168+
column: 27
169+
}
170+
]
171+
},
172+
{
173+
filename: 'test.vue',
174+
code: `<template><div :data-foo="\`\${foo}\`" /></template>`,
175+
output: `<template><div :data-foo="String(foo)" /></template>`,
176+
options: [
177+
{
178+
disallowTemplateShorthand: true
179+
}
180+
],
181+
errors: [
182+
{
183+
message: 'use `String(foo)` instead.',
184+
line: 1,
185+
column: 27
186+
}
187+
]
188+
}
189+
]
190+
})

0 commit comments

Comments
 (0)