Skip to content

Commit

Permalink
fix: [vue/component-name-in-template-casing] False positive for dynam…
Browse files Browse the repository at this point in the history
…ic component in template (#2069)
  • Loading branch information
horitaka authored Jan 7, 2023
1 parent e42f737 commit 4a08c07
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 3 deletions.
29 changes: 28 additions & 1 deletion lib/rules/component-name-in-template-casing.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@ const { toRegExp } = require('../utils/regexp')
const allowedCaseOptions = ['PascalCase', 'kebab-case']
const defaultCase = 'PascalCase'

/**
* Checks whether the given variable is the type-only import object.
* @param {Variable} variable
* @returns {boolean} `true` if the given variable is the type-only import.
*/
function isTypeOnlyImport(variable) {
if (variable.defs.length === 0) return false

return variable.defs.every((def) => {
if (def.type !== 'ImportBinding') {
return false
}
if (def.parent.importKind === 'type') {
// check for `import type Foo from './xxx'`
return true
}
if (def.node.type === 'ImportSpecifier' && def.node.importKind === 'type') {
// check for `import { type Foo } from './xxx'`
return true
}
return false
})
}

module.exports = {
meta: {
type: 'suggestion',
Expand Down Expand Up @@ -75,10 +99,13 @@ module.exports = {
(scope) => scope.type === 'module'
)
for (const variable of (moduleScope && moduleScope.variables) || []) {
registeredComponents.add(variable.name)
if (!isTypeOnlyImport(variable)) {
registeredComponents.add(variable.name)
}
}
}
}

/**
* Checks whether the given node is the verification target node.
* @param {VElement} node element node
Expand Down
134 changes: 132 additions & 2 deletions tests/lib/rules/component-name-in-template-casing.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
'use strict'

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

const tester = new RuleTester({
Expand Down Expand Up @@ -193,7 +194,43 @@ tester.run('component-name-in-template-casing', rule, {
</template>
`,
options: ['kebab-case', { globals: ['RouterView', 'router-link'] }]
}
},

// type-only imports
...(semver.gte(
require('@typescript-eslint/parser/package.json').version,
'5.0.0'
)
? [
{
code: `
<script setup lang="ts">
import type Foo from './Foo.vue'
import type { HelloWorld1 } from './components/HelloWorld'
import { type HelloWorld2 } from './components/HelloWorld2'
import type { HelloWorld as HelloWorld3 } from './components/HelloWorld3'
import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
import { type default as HelloWorld5 } from './components/HelloWorld5';
import { type Component } from 'vue';
</script>
<template>
<foo />
<hello-world1 />
<hello-world2 />
<hello-world3 />
<hello-world4 />
<hello-world5 />
<component />
</template>
`,
options: ['PascalCase', { registeredComponentsOnly: true }],
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
}
}
]
: [])
],
invalid: [
{
Expand Down Expand Up @@ -939,6 +976,99 @@ tester.run('component-name-in-template-casing', rule, {
column: 11
}
]
}
},
// type-only imports
...(semver.gte(
require('@typescript-eslint/parser/package.json').version,
'5.0.0'
)
? [
{
code: `
<script setup lang="ts">
import type Foo from './Foo.vue'
import type { HelloWorld1 } from './components/HelloWorld'
import { type HelloWorld2 } from './components/HelloWorld2'
import type { HelloWorld as HelloWorld3 } from './components/HelloWorld3'
import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
import { type default as HelloWorld5 } from './components/HelloWorld5';
import { type Component } from 'vue';
</script>
<template>
<foo />
<hello-world1 />
<hello-world2 />
<hello-world3 />
<hello-world4 />
<hello-world5 />
<component />
</template>
`,
options: ['PascalCase', { registeredComponentsOnly: false }],
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
},
output: `
<script setup lang="ts">
import type Foo from './Foo.vue'
import type { HelloWorld1 } from './components/HelloWorld'
import { type HelloWorld2 } from './components/HelloWorld2'
import type { HelloWorld as HelloWorld3 } from './components/HelloWorld3'
import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
import { type default as HelloWorld5 } from './components/HelloWorld5';
import { type Component } from 'vue';
</script>
<template>
<Foo />
<HelloWorld1 />
<HelloWorld2 />
<HelloWorld3 />
<HelloWorld4 />
<HelloWorld5 />
<Component />
</template>
`,
errors: [
{
message: 'Component name "foo" is not PascalCase.',
line: 13,
column: 17
},
{
message: 'Component name "hello-world1" is not PascalCase.',
line: 14,
column: 17
},
{
message: 'Component name "hello-world2" is not PascalCase.',
line: 15,
column: 17
},
{
message: 'Component name "hello-world3" is not PascalCase.',
line: 16,
column: 17
},
{
message: 'Component name "hello-world4" is not PascalCase.',
line: 17,
column: 17
},
{
message: 'Component name "hello-world5" is not PascalCase.',
line: 18,
column: 17
},
{
message: 'Component name "component" is not PascalCase.',
line: 19,
column: 17
}
]
}
]
: [])
]
})

0 comments on commit 4a08c07

Please sign in to comment.