Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
|:---|:--------|:------------|
| :wrench: | [vue/attributes-order](./docs/rules/attributes-order.md) | enforce order of attributes |
| :wrench: | [vue/html-quotes](./docs/rules/html-quotes.md) | enforce quotes style of HTML attributes |
| | [vue/match-component-file-name](./docs/rules/match-component-file-name.md) | require component name property to match its file name |
| | [vue/no-v-html](./docs/rules/no-v-html.md) | disallow use of v-html to prevent XSS attack |
| :wrench: | [vue/order-in-components](./docs/rules/order-in-components.md) | enforce order of properties in components |
| | [vue/this-in-template](./docs/rules/this-in-template.md) | enforce usage of `this` in template |
Expand Down
50 changes: 50 additions & 0 deletions docs/rules/match-component-file-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# require component name property to match its file name (vue/match-component-file-name)

- :gear: This rule is included in `"plugin:vue/recommended"`.

You can choose which file extension this rule should verify the component's name:

- `.jsx` (**default**): `jsx`
- `.jsx` and `.vue`: `both`

By default this rule will only verify components in a file with a `.jsx`
extension. Files with `.vue` extension uses their resgistered name by default.
The only use case where you need to specify a name for your component
in a `.vue` file is when implementing recursive components.

The option to verify both files extensions is added to increase
consistency in projects where its style guide requires every component
to have a `name` property, although, as stated above it is unnecessary.

## :book: Rule Details

This rule reports if a component `name` property does not match its file name.

:-1: Examples of **incorrect** code for this rule:

```jsx
// file name: src/MyComponent.jsx
export default {
name: 'MComponent', // note the missing y
render: () {
return <h1>Hello world</h1>
}
}
```

:+1: Examples of **correct** code for this rule:

```jsx
// file name: src/MyComponent.jsx
export default {
name: 'MyComponent',
render: () {
return <h1>Hello world</h1>
}
}
```

## :wrench: Options

- `"jsx"` (default) ... verify components in files with `.jsx` extension.
- `"both"` ... verify components in files with both `.jsx` and `.vue` extensions.
1 change: 1 addition & 0 deletions lib/configs/recommended.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
rules: {
'vue/attributes-order': 'error',
'vue/html-quotes': 'error',
'vue/match-component-file-name': 'error',
'vue/no-v-html': 'error',
'vue/order-in-components': 'error',
'vue/this-in-template': 'error'
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
'html-quotes': require('./rules/html-quotes'),
'html-self-closing': require('./rules/html-self-closing'),
'jsx-uses-vars': require('./rules/jsx-uses-vars'),
'match-component-file-name': require('./rules/match-component-file-name'),
'max-attributes-per-line': require('./rules/max-attributes-per-line'),
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'),
Expand Down
57 changes: 57 additions & 0 deletions lib/rules/match-component-file-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @fileoverview Require component name property to match its file name
* @author Rodrigo Pedra Brum <[email protected]>
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

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

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: 'require component name property to match its file name',
category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/match-component-file-name.md'
},
fixable: null,
schema: [
{ enum: ['jsx', 'both'] }
]
},

create (context) {
return utils.executeOnVueComponent(context, (object) => {
const nameProperty = object.properties.find((prop) => prop.key.name === 'name')

if (!nameProperty) {
return
}

const allowedExtensions = context.options[0] || 'jsx'
const name = nameProperty.value.value
const [, filename, extension] = /^(.+?)\.(.*)$/g.exec(path.basename(context.getFilename()))

if (extension === 'vue' && allowedExtensions !== 'both') {
return
}

if (name !== filename) {
context.report({
obj: object,
loc: object.loc,
message: 'Component name should match file name ({{filename}} / {{name}}).',
data: { filename, name }
})
}
})
}
}
155 changes: 155 additions & 0 deletions tests/lib/rules/match-component-file-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* @fileoverview Require component name property to match its file name
* @author Rodrigo Pedra Brum <[email protected]>
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

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

const jsxRuleTester = new RuleTester({
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
}
})

const vueRuleTester = new RuleTester({
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2015,
sourceType: 'module'
}
})

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

jsxRuleTester.run('match-component-file-name', rule, {
valid: [
{
filename: 'MyComponent.jsx',
code: `
export default {
name: 'MyComponent',
render() { return <div /> }
}
`
},
{
filename: 'MyComponent.jsx',
code: `
export default {
name: 'MyComponent',
render() { return <div /> }
}
`,
options: ['jsx']
},
{
filename: 'MyComponent.jsx',
code: `
export default {
name: 'MyComponent',
render() { return <div /> }
}
`,
options: ['both']
}
],

invalid: [
{
filename: 'MyComponent.jsx',
code: `
export default {
name: 'MComponent',
render() { return <div /> }
}
`,
errors: [{
message: 'Component name should match file name (MyComponent / MComponent).'
}]
},
{
filename: 'MyComponent.jsx',
code: `
export default {
name: 'MComponent',
render() { return <div /> }
}
`,
options: ['jsx'],
errors: [{
message: 'Component name should match file name (MyComponent / MComponent).'
}]
},
{
filename: 'MyComponent.jsx',
code: `
export default {
name: 'MComponent',
render() { return <div /> }
}
`,
options: ['both'],
errors: [{
message: 'Component name should match file name (MyComponent / MComponent).'
}]
}
]
})

vueRuleTester.run('match-component-file-name', rule, {
valid: [
{
filename: 'MyComponent.vue',
code: `
<script>
export default {
name: 'MComponent',
template: '<div />'
}
</script>
` // missing ["both"] option, so the file is ignored
},
{
filename: 'MyComponent.vue',
code: `
<script>
export default {
name: 'MyComponent',
template: '<div />'
}
</script>
`,
options: ['both']
}
],

invalid: [
{
filename: 'MyComponent.vue',
code: `
<script>
export default {
name: 'MComponent',
template: '<div />'
}
</script>
`,
options: ['both'],
errors: [{
message: 'Component name should match file name (MyComponent / MComponent).'
}]
}
]
})