Skip to content

Commit

Permalink
Fix 1786 (#1831)
Browse files Browse the repository at this point in the history
* 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 <florian-edelmann@online.de>

* Don't change utils if you can avoid it

* update docs

* Update docs/rules/match-component-import-name.md

Co-authored-by: Flo Edelmann <florian-edelmann@online.de>

* Update docs/rules/match-component-import-name.md

Co-authored-by: Flo Edelmann <florian-edelmann@online.de>

* Update docs/rules/match-component-import-name.md

Co-authored-by: Flo Edelmann <florian-edelmann@online.de>

* Update docs/rules/match-component-import-name.md

Co-authored-by: Flo Edelmann <florian-edelmann@online.de>

* remove case option

* remove stray newline

* Remove prefix; handle computed properties

* but who lints the linters

Co-authored-by: Flo Edelmann <florian-edelmann@online.de>
  • Loading branch information
doug-wade and FloEdelmann committed Apr 12, 2022
1 parent 1e375bc commit 46da539
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -317,6 +317,7 @@ For example:
| [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: |
| [vue/html-comment-indent](./html-comment-indent.md) | enforce consistent indentation in HTML comments | :wrench: |
| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | |
| [vue/match-component-import-name](./match-component-import-name.md) | require the registered component name to match the imported component name | |
| [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: |
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: |
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
Expand Down
45 changes: 45 additions & 0 deletions docs/rules/match-component-import-name.md
@@ -0,0 +1,45 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/match-component-import-name
description: require the registered component name to match the imported component name
---
# vue/match-component-import-name

> require the registered component name to match the imported component name
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

## :book: Rule Details

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).

<eslint-code-block :rules="{'vue/match-component-file-name': ['error']}">

```vue
<script>
export default {
components: {
/* ✓ GOOD */
AppButton,
AppButton: AppButton,
'app-button': AppButton,
/* ✗ BAD */
SomeOtherName: AppButton,
'some-other-name': AppButton
}
}
</script>
```

</eslint-code-block>

## :couple: Related Rules

- [vue/component-definition-name-casing](./component-definition-name-casing.md)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/match-component-import-name.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/match-component-import-name.js)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -47,6 +47,7 @@ module.exports = {
'key-spacing': require('./rules/key-spacing'),
'keyword-spacing': require('./rules/keyword-spacing'),
'match-component-file-name': require('./rules/match-component-file-name'),
'match-component-import-name': require('./rules/match-component-import-name'),
'max-attributes-per-line': require('./rules/max-attributes-per-line'),
'max-len': require('./rules/max-len'),
'multi-word-component-names': require('./rules/multi-word-component-names'),
Expand Down
79 changes: 79 additions & 0 deletions lib/rules/match-component-import-name.js
@@ -0,0 +1,79 @@
/**
* @author Doug Wade <douglas.b.wade@gmail.com>
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../utils')
const casing = require('../utils/casing')

module.exports = {
meta: {
type: 'problem',
schema: [],
docs: {
description:
'require the registered component name to match the imported component name',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/match-component-import-name.html'
},
fixable: null,
messages: {
unexpected:
'Component alias {{importedName}} should be one of: {{expectedName}}.'
}
},
/**
* @param {RuleContext} context
* @returns {RuleListener}
*/
create(context) {
/**
* @param {Identifier} identifier
* @return {Array<String>}
*/
function getExpectedNames(identifier) {
return [
casing.pascalCase(identifier.name),
casing.kebabCase(identifier.name)
]
}

return utils.executeOnVueComponent(context, (obj) => {
const components = utils.findProperty(obj, 'components')
if (
!components ||
!components.value ||
components.value.type !== 'ObjectExpression'
) {
return
}

components.value.properties.forEach(
/** @param {Property | SpreadElement} property */
(property) => {
if (
property.type === 'SpreadElement' ||
property.value.type !== 'Identifier' ||
property.computed === true
) {
return
}

const importedName = utils.getStaticPropertyName(property) || ''
const expectedNames = getExpectedNames(property.value)
if (!expectedNames.includes(importedName)) {
context.report({
node: property,
messageId: 'unexpected',
data: {
importedName,
expectedName: expectedNames.join(', ')
}
})
}
}
)
})
}
}
73 changes: 73 additions & 0 deletions tests/lib/rules/match-component-import-name.js
@@ -0,0 +1,73 @@
/**
* @author Doug Wade
* See LICENSE file in root directory for full license.
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/match-component-import-name')

const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
}
})

tester.run('match-component-import-name', rule, {
valid: [
{
filename: 'test.vue',
code: `
<script> export default { components: { ValidImport } } </script>
`
},
{
filename: 'test.vue',
code: `
<script> export default { components: { ValidImport: ValidImport } } </script>
`
},
{
filename: 'test.vue',
code: `
<script> export default { components: { 'valid-import': ValidImport } } </script>
`
},
{
filename: 'test.vue',
code: `
<script> export default { components: { ValidImport, ...SpreadImport } } </script>
`
},
{
filename: 'test.vue',
code: `
<script> export default { components: { 'valid-import': ValidImport, [computedImport]: ComputedImport } } </script>
`
},
{
filename: 'test.vue',
code: `
<script> export default { components: { ValidImport, [differentComputedImport]: ComputedImport } } </script>
`
}
],
invalid: [
{
filename: 'test.vue',
code: `
<script> export default { components: { InvalidExport: SomeRandomName } } </script>
`,
errors: [
{
message:
'Component alias InvalidExport should be one of: SomeRandomName, some-random-name.',
line: 2,
column: 47
}
]
}
]
})

0 comments on commit 46da539

Please sign in to comment.