Skip to content

Add name-property-casing rule. #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
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
37 changes: 37 additions & 0 deletions docs/rules/name-property-casing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Requires specific casing for the name property in Vue components (name-property-casing)

Define a style for the `name` property casing for consistency purposes.

## :book: Rule Details

:+1: Examples of **correct** code for `PascalCase`:

```js
export default {
name: 'MyComponent'
}
```

:+1: Examples of **correct** code for `kebab-case`:

```js
export default {
name: 'my-component'
}
```

:+1: Examples of **correct** code for `camelCase`:

```js
export default {
name: 'myComponent'
}
```

## :wrench: Options

Default casing is set to `PascalCase`

```
'vue/name-property-casing': [2, 'camelCase|kebab-case|PascalCase']
```
99 changes: 99 additions & 0 deletions lib/rules/name-property-casing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* @fileoverview Requires specific casing for the name property in Vue components
* @author Armano
*/
'use strict'

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

function kebabCase (str) {
return str
.replace(/([a-z])([A-Z])/g, match => match[0] + '-' + match[1])
.replace(/[^a-zA-Z:]+/g, '-')
.toLowerCase()
}

function camelCase (str) {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => (
index === 0 ? letter.toLowerCase() : letter.toUpperCase())
)
.replace(/[^a-zA-Z:]+/g, '')
}

function pascalCase (str) {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => letter.toUpperCase())
.replace(/[^a-zA-Z:]+/g, '')
}

const allowedCaseOptions = [
'camelCase',
'kebab-case',
'PascalCase'
]

const convertersMap = {
'kebab-case': kebabCase,
'camelCase': camelCase,
'PascalCase': pascalCase
}

function getConverter (name) {
return convertersMap[name] || pascalCase
}

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

function create (context) {
const options = context.options[0]
const caseType = allowedCaseOptions.indexOf(options) !== -1 ? options : 'PascalCase'

// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------

return utils.executeOnVueComponent(context, (obj) => {
const node = obj.properties
.filter(item => (
item.type === 'Property' &&
item.key.name === 'name' &&
item.value.type === 'Literal'
))[0]

if (!node) return

const value = getConverter(caseType)(node.value.value)
if (value !== node.value.value) {
context.report({
node: node.value,
message: 'Property name "{{value}}" is not {{caseType}}.',
data: {
value: node.value.value,
caseType: caseType
},
fix: fixer => fixer.replaceText(node.value, node.value.raw.replace(node.value.value, value))
})
}
})
}

module.exports = {
meta: {
docs: {
description: 'Requires specific casing for the name property in Vue components',
category: 'Stylistic Issues',
recommended: false
},
fixable: 'code', // or "code" or "whitespace"
schema: [
{
enum: allowedCaseOptions
}
]
},

create
}
170 changes: 170 additions & 0 deletions tests/lib/rules/name-property-casing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* @fileoverview Define a style for the name property casing for consistency purposes
* @author Armano
*/
'use strict'

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

const rule = require('../../../lib/rules/name-property-casing')
const RuleTester = require('eslint').RuleTester

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

const ruleTester = new RuleTester()
ruleTester.run('name-property-casing', rule, {

valid: [
{
filename: 'test.vue',
code: `
export default {
}
`,
options: ['camelCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
},
{
filename: 'test.vue',
code: `
export default {
name: 'fooBar'
}
`,
options: ['camelCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
},
{
filename: 'test.vue',
code: `
export default {
name: 'FooBar'
}
`,
options: ['PascalCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
},
{
filename: 'test.vue',
code: `
export default {
name: 'foo-bar'
}
`,
options: ['kebab-case'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
}
],

invalid: [
{
filename: 'test.vue',
code: `
export default {
name: 'foo-bar'
}
`,
options: ['camelCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Property name "foo-bar" is not camelCase.',
type: 'Literal',
line: 3
}]
},
{
filename: 'test.vue',
code: `
export default {
name: 'foo bar'
}
`,
options: ['PascalCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Property name "foo bar" is not PascalCase.',
type: 'Literal',
line: 3
}]
},
{
filename: 'test.vue',
code: `
export default {
name: 'foo!bar'
}
`,
options: ['camelCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Property name "foo!bar" is not camelCase.',
type: 'Literal',
line: 3
}]
},
{
filename: 'test.js',
code: `
new Vue({
name: 'foo!bar'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add one more test with foo_bar

})
`,
options: ['camelCase'],
parserOptions: { ecmaVersion: 6 },
errors: [{
message: 'Property name "foo!bar" is not camelCase.',
type: 'Literal',
line: 3
}]
},
{
filename: 'test.vue',
code: `
export default {
name: 'foo_bar'
}
`,
options: ['camelCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Property name "foo_bar" is not camelCase.',
type: 'Literal',
line: 3
}]
},
{
filename: 'test.vue',
code: `
export default {
name: 'foo_bar'
}
`,
options: ['PascalCase'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Property name "foo_bar" is not PascalCase.',
type: 'Literal',
line: 3
}]
},
{
filename: 'test.vue',
code: `
export default {
name: 'foo_bar'
}
`,
options: ['kebab-case'],
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Property name "foo_bar" is not kebab-case.',
type: 'Literal',
line: 3
}]
}
]
})