Skip to content

Commit 4a08c07

Browse files
authored
fix: [vue/component-name-in-template-casing] False positive for dynamic component in template (#2069)
1 parent e42f737 commit 4a08c07

File tree

2 files changed

+160
-3
lines changed

2 files changed

+160
-3
lines changed

Diff for: lib/rules/component-name-in-template-casing.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,30 @@ const { toRegExp } = require('../utils/regexp')
1111
const allowedCaseOptions = ['PascalCase', 'kebab-case']
1212
const defaultCase = 'PascalCase'
1313

14+
/**
15+
* Checks whether the given variable is the type-only import object.
16+
* @param {Variable} variable
17+
* @returns {boolean} `true` if the given variable is the type-only import.
18+
*/
19+
function isTypeOnlyImport(variable) {
20+
if (variable.defs.length === 0) return false
21+
22+
return variable.defs.every((def) => {
23+
if (def.type !== 'ImportBinding') {
24+
return false
25+
}
26+
if (def.parent.importKind === 'type') {
27+
// check for `import type Foo from './xxx'`
28+
return true
29+
}
30+
if (def.node.type === 'ImportSpecifier' && def.node.importKind === 'type') {
31+
// check for `import { type Foo } from './xxx'`
32+
return true
33+
}
34+
return false
35+
})
36+
}
37+
1438
module.exports = {
1539
meta: {
1640
type: 'suggestion',
@@ -75,10 +99,13 @@ module.exports = {
7599
(scope) => scope.type === 'module'
76100
)
77101
for (const variable of (moduleScope && moduleScope.variables) || []) {
78-
registeredComponents.add(variable.name)
102+
if (!isTypeOnlyImport(variable)) {
103+
registeredComponents.add(variable.name)
104+
}
79105
}
80106
}
81107
}
108+
82109
/**
83110
* Checks whether the given node is the verification target node.
84111
* @param {VElement} node element node

Diff for: tests/lib/rules/component-name-in-template-casing.js

+132-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
'use strict'
55

66
const rule = require('../../../lib/rules/component-name-in-template-casing')
7+
const semver = require('semver')
78
const RuleTester = require('eslint').RuleTester
89

910
const tester = new RuleTester({
@@ -193,7 +194,43 @@ tester.run('component-name-in-template-casing', rule, {
193194
</template>
194195
`,
195196
options: ['kebab-case', { globals: ['RouterView', 'router-link'] }]
196-
}
197+
},
198+
199+
// type-only imports
200+
...(semver.gte(
201+
require('@typescript-eslint/parser/package.json').version,
202+
'5.0.0'
203+
)
204+
? [
205+
{
206+
code: `
207+
<script setup lang="ts">
208+
import type Foo from './Foo.vue'
209+
import type { HelloWorld1 } from './components/HelloWorld'
210+
import { type HelloWorld2 } from './components/HelloWorld2'
211+
import type { HelloWorld as HelloWorld3 } from './components/HelloWorld3'
212+
import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
213+
import { type default as HelloWorld5 } from './components/HelloWorld5';
214+
import { type Component } from 'vue';
215+
</script>
216+
217+
<template>
218+
<foo />
219+
<hello-world1 />
220+
<hello-world2 />
221+
<hello-world3 />
222+
<hello-world4 />
223+
<hello-world5 />
224+
<component />
225+
</template>
226+
`,
227+
options: ['PascalCase', { registeredComponentsOnly: true }],
228+
parserOptions: {
229+
parser: require.resolve('@typescript-eslint/parser')
230+
}
231+
}
232+
]
233+
: [])
197234
],
198235
invalid: [
199236
{
@@ -939,6 +976,99 @@ tester.run('component-name-in-template-casing', rule, {
939976
column: 11
940977
}
941978
]
942-
}
979+
},
980+
// type-only imports
981+
...(semver.gte(
982+
require('@typescript-eslint/parser/package.json').version,
983+
'5.0.0'
984+
)
985+
? [
986+
{
987+
code: `
988+
<script setup lang="ts">
989+
import type Foo from './Foo.vue'
990+
import type { HelloWorld1 } from './components/HelloWorld'
991+
import { type HelloWorld2 } from './components/HelloWorld2'
992+
import type { HelloWorld as HelloWorld3 } from './components/HelloWorld3'
993+
import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
994+
import { type default as HelloWorld5 } from './components/HelloWorld5';
995+
import { type Component } from 'vue';
996+
</script>
997+
998+
<template>
999+
<foo />
1000+
<hello-world1 />
1001+
<hello-world2 />
1002+
<hello-world3 />
1003+
<hello-world4 />
1004+
<hello-world5 />
1005+
<component />
1006+
</template>
1007+
`,
1008+
options: ['PascalCase', { registeredComponentsOnly: false }],
1009+
parserOptions: {
1010+
parser: require.resolve('@typescript-eslint/parser')
1011+
},
1012+
output: `
1013+
<script setup lang="ts">
1014+
import type Foo from './Foo.vue'
1015+
import type { HelloWorld1 } from './components/HelloWorld'
1016+
import { type HelloWorld2 } from './components/HelloWorld2'
1017+
import type { HelloWorld as HelloWorld3 } from './components/HelloWorld3'
1018+
import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
1019+
import { type default as HelloWorld5 } from './components/HelloWorld5';
1020+
import { type Component } from 'vue';
1021+
</script>
1022+
1023+
<template>
1024+
<Foo />
1025+
<HelloWorld1 />
1026+
<HelloWorld2 />
1027+
<HelloWorld3 />
1028+
<HelloWorld4 />
1029+
<HelloWorld5 />
1030+
<Component />
1031+
</template>
1032+
`,
1033+
errors: [
1034+
{
1035+
message: 'Component name "foo" is not PascalCase.',
1036+
line: 13,
1037+
column: 17
1038+
},
1039+
{
1040+
message: 'Component name "hello-world1" is not PascalCase.',
1041+
line: 14,
1042+
column: 17
1043+
},
1044+
{
1045+
message: 'Component name "hello-world2" is not PascalCase.',
1046+
line: 15,
1047+
column: 17
1048+
},
1049+
{
1050+
message: 'Component name "hello-world3" is not PascalCase.',
1051+
line: 16,
1052+
column: 17
1053+
},
1054+
{
1055+
message: 'Component name "hello-world4" is not PascalCase.',
1056+
line: 17,
1057+
column: 17
1058+
},
1059+
{
1060+
message: 'Component name "hello-world5" is not PascalCase.',
1061+
line: 18,
1062+
column: 17
1063+
},
1064+
{
1065+
message: 'Component name "component" is not PascalCase.',
1066+
line: 19,
1067+
column: 17
1068+
}
1069+
]
1070+
}
1071+
]
1072+
: [])
9431073
]
9441074
})

0 commit comments

Comments
 (0)