Skip to content

Commit 46da539

Browse files
Fix 1786 (#1831)
* Fix #1786: Add rule match-component-import-name * Write the documentation for match-component-import-name * fix(1786): Respond to PR feedback * Update lib/rules/match-component-import-name.js Co-authored-by: Flo Edelmann <[email protected]> * Don't change utils if you can avoid it * update docs * Update docs/rules/match-component-import-name.md Co-authored-by: Flo Edelmann <[email protected]> * Update docs/rules/match-component-import-name.md Co-authored-by: Flo Edelmann <[email protected]> * Update docs/rules/match-component-import-name.md Co-authored-by: Flo Edelmann <[email protected]> * Update docs/rules/match-component-import-name.md Co-authored-by: Flo Edelmann <[email protected]> * remove case option * remove stray newline * Remove prefix; handle computed properties * but who lints the linters Co-authored-by: Flo Edelmann <[email protected]>
1 parent 1e375bc commit 46da539

File tree

5 files changed

+199
-0
lines changed

5 files changed

+199
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ For example:
317317
| [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: |
318318
| [vue/html-comment-indent](./html-comment-indent.md) | enforce consistent indentation in HTML comments | :wrench: |
319319
| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | |
320+
| [vue/match-component-import-name](./match-component-import-name.md) | require the registered component name to match the imported component name | |
320321
| [vue/new-line-between-multi-line-property](./new-line-between-multi-line-property.md) | enforce new lines between multi-line properties in Vue components | :wrench: |
321322
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: |
322323
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/match-component-import-name
5+
description: require the registered component name to match the imported component name
6+
---
7+
# vue/match-component-import-name
8+
9+
> require the registered component name to match the imported component name
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
By default, this rule will validate that the imported name matches the name of the components object property identifer. Note that "matches" means that the imported name matches either the PascalCase or kebab-case version of the components object property identifer. If you would like to enforce that it must match only one of PascalCase or kebab-case, use this rule in conjunction with the rule [vue/component-definition-name-casing](./component-definition-name-casing.md).
16+
17+
<eslint-code-block :rules="{'vue/match-component-file-name': ['error']}">
18+
19+
```vue
20+
<script>
21+
export default {
22+
components: {
23+
/* ✓ GOOD */
24+
AppButton,
25+
AppButton: AppButton,
26+
'app-button': AppButton,
27+
28+
/* ✗ BAD */
29+
SomeOtherName: AppButton,
30+
'some-other-name': AppButton
31+
}
32+
}
33+
</script>
34+
```
35+
36+
</eslint-code-block>
37+
38+
## :couple: Related Rules
39+
40+
- [vue/component-definition-name-casing](./component-definition-name-casing.md)
41+
42+
## :mag: Implementation
43+
44+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/match-component-import-name.js)
45+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/match-component-import-name.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ module.exports = {
4747
'key-spacing': require('./rules/key-spacing'),
4848
'keyword-spacing': require('./rules/keyword-spacing'),
4949
'match-component-file-name': require('./rules/match-component-file-name'),
50+
'match-component-import-name': require('./rules/match-component-import-name'),
5051
'max-attributes-per-line': require('./rules/max-attributes-per-line'),
5152
'max-len': require('./rules/max-len'),
5253
'multi-word-component-names': require('./rules/multi-word-component-names'),
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @author Doug Wade <[email protected]>
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
const casing = require('../utils/casing')
9+
10+
module.exports = {
11+
meta: {
12+
type: 'problem',
13+
schema: [],
14+
docs: {
15+
description:
16+
'require the registered component name to match the imported component name',
17+
categories: undefined,
18+
url: 'https://eslint.vuejs.org/rules/match-component-import-name.html'
19+
},
20+
fixable: null,
21+
messages: {
22+
unexpected:
23+
'Component alias {{importedName}} should be one of: {{expectedName}}.'
24+
}
25+
},
26+
/**
27+
* @param {RuleContext} context
28+
* @returns {RuleListener}
29+
*/
30+
create(context) {
31+
/**
32+
* @param {Identifier} identifier
33+
* @return {Array<String>}
34+
*/
35+
function getExpectedNames(identifier) {
36+
return [
37+
casing.pascalCase(identifier.name),
38+
casing.kebabCase(identifier.name)
39+
]
40+
}
41+
42+
return utils.executeOnVueComponent(context, (obj) => {
43+
const components = utils.findProperty(obj, 'components')
44+
if (
45+
!components ||
46+
!components.value ||
47+
components.value.type !== 'ObjectExpression'
48+
) {
49+
return
50+
}
51+
52+
components.value.properties.forEach(
53+
/** @param {Property | SpreadElement} property */
54+
(property) => {
55+
if (
56+
property.type === 'SpreadElement' ||
57+
property.value.type !== 'Identifier' ||
58+
property.computed === true
59+
) {
60+
return
61+
}
62+
63+
const importedName = utils.getStaticPropertyName(property) || ''
64+
const expectedNames = getExpectedNames(property.value)
65+
if (!expectedNames.includes(importedName)) {
66+
context.report({
67+
node: property,
68+
messageId: 'unexpected',
69+
data: {
70+
importedName,
71+
expectedName: expectedNames.join(', ')
72+
}
73+
})
74+
}
75+
}
76+
)
77+
})
78+
}
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @author Doug Wade
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('eslint').RuleTester
8+
const rule = require('../../../lib/rules/match-component-import-name')
9+
10+
const tester = new RuleTester({
11+
parser: require.resolve('vue-eslint-parser'),
12+
parserOptions: {
13+
ecmaVersion: 2020,
14+
sourceType: 'module'
15+
}
16+
})
17+
18+
tester.run('match-component-import-name', rule, {
19+
valid: [
20+
{
21+
filename: 'test.vue',
22+
code: `
23+
<script> export default { components: { ValidImport } } </script>
24+
`
25+
},
26+
{
27+
filename: 'test.vue',
28+
code: `
29+
<script> export default { components: { ValidImport: ValidImport } } </script>
30+
`
31+
},
32+
{
33+
filename: 'test.vue',
34+
code: `
35+
<script> export default { components: { 'valid-import': ValidImport } } </script>
36+
`
37+
},
38+
{
39+
filename: 'test.vue',
40+
code: `
41+
<script> export default { components: { ValidImport, ...SpreadImport } } </script>
42+
`
43+
},
44+
{
45+
filename: 'test.vue',
46+
code: `
47+
<script> export default { components: { 'valid-import': ValidImport, [computedImport]: ComputedImport } } </script>
48+
`
49+
},
50+
{
51+
filename: 'test.vue',
52+
code: `
53+
<script> export default { components: { ValidImport, [differentComputedImport]: ComputedImport } } </script>
54+
`
55+
}
56+
],
57+
invalid: [
58+
{
59+
filename: 'test.vue',
60+
code: `
61+
<script> export default { components: { InvalidExport: SomeRandomName } } </script>
62+
`,
63+
errors: [
64+
{
65+
message:
66+
'Component alias InvalidExport should be one of: SomeRandomName, some-random-name.',
67+
line: 2,
68+
column: 47
69+
}
70+
]
71+
}
72+
]
73+
})

0 commit comments

Comments
 (0)