Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 99bbe07

Browse filesBrowse files
lozinskyota-meshi
andauthored
Add vue/no-implicit-coercion rule (#2639)
Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
1 parent f62fde6 commit 99bbe07
Copy full SHA for 99bbe07

File tree

4 files changed

+307
-0
lines changed
Filter options

4 files changed

+307
-0
lines changed

‎docs/rules/no-implicit-coercion.md

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

‎lib/index.js

Copy file name to clipboardExpand all lines: lib/index.js
+1Lines changed: 1 addition & 0 deletions
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'),

‎lib/rules/no-implicit-coercion.js

Copy file name to clipboard
+12Lines changed: 12 additions & 0 deletions
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+
})
+263Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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, ESLint } = require('../../eslint-compat')
8+
const semver = require('semver')
9+
const rule = require('../../../lib/rules/no-implicit-coercion')
10+
11+
/**
12+
* @param {string} suggestedCode
13+
* @returns {string}
14+
*/
15+
function getExpectedErrorMessage(suggestedCode) {
16+
return semver.gte(ESLint.version, '9.0.0')
17+
? `Unexpected implicit coercion encountered. Use \`${suggestedCode}\` instead.`
18+
: `use \`${suggestedCode}\` instead.`
19+
}
20+
21+
const tester = new RuleTester({
22+
languageOptions: {
23+
parser: require('vue-eslint-parser'),
24+
ecmaVersion: 2020,
25+
sourceType: 'module'
26+
}
27+
})
28+
29+
tester.run('no-implicit-coercion', rule, {
30+
valid: [
31+
`<template><div :data-foo="Boolean(foo)" /></template>`,
32+
`<template><div :data-foo="foo.indexOf('.') !== -1" /></template>`,
33+
{
34+
filename: 'test.vue',
35+
code: `<template><div :data-foo="!!foo" /></template>`,
36+
options: [
37+
{
38+
boolean: false
39+
}
40+
]
41+
},
42+
{
43+
filename: 'test.vue',
44+
code: `<template><div :data-foo="~foo.indexOf('.')" /></template>`,
45+
options: [
46+
{
47+
boolean: false
48+
}
49+
]
50+
},
51+
`<template><div :data-foo="Number(foo)" /></template>`,
52+
...(semver.gte(ESLint.version, '8.28.0')
53+
? [`<template><div :data-foo="foo * 1/4" /></template>`]
54+
: []),
55+
{
56+
filename: 'test.vue',
57+
code: `<template><div :data-foo="+foo" /></template>`,
58+
options: [
59+
{
60+
number: false
61+
}
62+
]
63+
},
64+
{
65+
filename: 'test.vue',
66+
code: `<template><div :data-foo="1 * foo" /></template>`,
67+
options: [
68+
{
69+
number: false
70+
}
71+
]
72+
},
73+
`<template><div :data-foo="String(foo)" /></template>`,
74+
`<template><div :data-foo="\`\${foo}\`" /></template>`,
75+
{
76+
filename: 'test.vue',
77+
code: `<template><div :data-foo="'' + foo" /></template>`,
78+
options: [
79+
{
80+
string: false
81+
}
82+
]
83+
},
84+
{
85+
filename: 'test.vue',
86+
code: `<template><div :data-foo="\`\` + foo" /></template>`,
87+
options: [
88+
{
89+
string: false
90+
}
91+
]
92+
},
93+
{
94+
filename: 'test.vue',
95+
code: `<template><div :data-foo="!!foo" /></template>`,
96+
options: [
97+
{
98+
allow: ['!!']
99+
}
100+
]
101+
},
102+
{
103+
filename: 'test.vue',
104+
code: `<template><div :data-foo="~foo.indexOf('.')" /></template>`,
105+
options: [
106+
{
107+
allow: ['~']
108+
}
109+
]
110+
}
111+
],
112+
invalid: [
113+
{
114+
filename: 'test.vue',
115+
code: `<template><div :data-foo="!!foo" /></template>`,
116+
output: `<template><div :data-foo="Boolean(foo)" /></template>`,
117+
errors: [
118+
{
119+
message: getExpectedErrorMessage('Boolean(foo)'),
120+
line: 1,
121+
column: 27
122+
}
123+
]
124+
},
125+
{
126+
filename: 'test.vue',
127+
code: `<template><div :data-foo="~foo.indexOf('.')" /></template>`,
128+
output: null,
129+
errors: [
130+
{
131+
message: getExpectedErrorMessage("foo.indexOf('.') !== -1"),
132+
line: 1,
133+
column: 27
134+
}
135+
]
136+
},
137+
{
138+
filename: 'test.vue',
139+
code: `<template><div :data-foo="+foo" /></template>`,
140+
output: semver.gte(ESLint.version, '9.0.0')
141+
? null
142+
: `<template><div :data-foo="Number(foo)" /></template>`,
143+
errors: [
144+
{
145+
message: getExpectedErrorMessage('Number(foo)'),
146+
line: 1,
147+
column: 27,
148+
suggestions: semver.gte(ESLint.version, '9.0.0')
149+
? [
150+
{
151+
messageId: 'useRecommendation',
152+
data: { recommendation: 'Number(foo)' },
153+
output: '<template><div :data-foo="Number(foo)" /></template>'
154+
}
155+
]
156+
: []
157+
}
158+
]
159+
},
160+
{
161+
filename: 'test.vue',
162+
code: `<template><div :data-foo="1 * foo" /></template>`,
163+
output: semver.gte(ESLint.version, '9.0.0')
164+
? null
165+
: `<template><div :data-foo="Number(foo)" /></template>`,
166+
errors: [
167+
{
168+
message: getExpectedErrorMessage('Number(foo)'),
169+
line: 1,
170+
column: 27,
171+
suggestions: semver.gte(ESLint.version, '9.0.0')
172+
? [
173+
{
174+
messageId: 'useRecommendation',
175+
data: { recommendation: 'Number(foo)' },
176+
output: '<template><div :data-foo="Number(foo)" /></template>'
177+
}
178+
]
179+
: []
180+
}
181+
]
182+
},
183+
{
184+
filename: 'test.vue',
185+
code: `<template><div :data-foo="'' + foo" /></template>`,
186+
output: semver.gte(ESLint.version, '9.0.0')
187+
? null
188+
: `<template><div :data-foo="String(foo)" /></template>`,
189+
errors: [
190+
{
191+
message: getExpectedErrorMessage('String(foo)'),
192+
line: 1,
193+
column: 27,
194+
suggestions: semver.gte(ESLint.version, '9.0.0')
195+
? [
196+
{
197+
messageId: 'useRecommendation',
198+
data: { recommendation: 'String(foo)' },
199+
output: '<template><div :data-foo="String(foo)" /></template>'
200+
}
201+
]
202+
: []
203+
}
204+
]
205+
},
206+
{
207+
filename: 'test.vue',
208+
code: `<template><div :data-foo="\`\` + foo" /></template>`,
209+
output: semver.gte(ESLint.version, '9.0.0')
210+
? null
211+
: `<template><div :data-foo="String(foo)" /></template>`,
212+
errors: [
213+
{
214+
message: getExpectedErrorMessage('String(foo)'),
215+
line: 1,
216+
column: 27,
217+
suggestions: semver.gte(ESLint.version, '9.0.0')
218+
? [
219+
{
220+
messageId: 'useRecommendation',
221+
data: { recommendation: 'String(foo)' },
222+
output: '<template><div :data-foo="String(foo)" /></template>'
223+
}
224+
]
225+
: []
226+
}
227+
]
228+
},
229+
...(semver.gte(ESLint.version, '7.24.0')
230+
? [
231+
{
232+
filename: 'test.vue',
233+
code: `<template><div :data-foo="\`\${foo}\`" /></template>`,
234+
output: semver.gte(ESLint.version, '9.0.0')
235+
? null
236+
: `<template><div :data-foo="String(foo)" /></template>`,
237+
options: [
238+
{
239+
disallowTemplateShorthand: true
240+
}
241+
],
242+
errors: [
243+
{
244+
message: getExpectedErrorMessage('String(foo)'),
245+
line: 1,
246+
column: 27,
247+
suggestions: semver.gte(ESLint.version, '9.0.0')
248+
? [
249+
{
250+
messageId: 'useRecommendation',
251+
data: { recommendation: 'String(foo)' },
252+
output:
253+
'<template><div :data-foo="String(foo)" /></template>'
254+
}
255+
]
256+
: []
257+
}
258+
]
259+
}
260+
]
261+
: [])
262+
]
263+
})

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.