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 2 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 @@
# Name property casing for consistency purposes (name-property-casing)
Copy link
Member

Choose a reason for hiding this comment

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

Requires specific casing for the name property in Vue components (name-property-casing)


Define a style for the `name` property casing for consistency purposes
Copy link
Member

Choose a reason for hiding this comment

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

Missing dot at the end of the sentence


## :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']
Copy link
Member

Choose a reason for hiding this comment

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

camelCase|kebab-case|PascalCase

```
79 changes: 79 additions & 0 deletions lib/rules/name-property-casing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @fileoverview Name property casing for consistency purposes
Copy link
Member

Choose a reason for hiding this comment

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

Use the same title as in documentation

* @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(/\s+/g, '-').toLowerCase()
}

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

function pascalCase (str) {
str = camelCase(str)
return str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : ''
}

function convertCase (str, caseType) {
if (caseType === 'kebab-case') {
Copy link
Member

Choose a reason for hiding this comment

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

Ideal case for switch/case. Or better you can create map with converters:

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

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

And use it like so:

function convertCase (str, caseType) {
  return getConverter(caseType)(str)
}

WDYT?

return kebabCase(str)
} else if (caseType === 'PascalCase') {
return pascalCase(str)
}
return camelCase(str)
}

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

function create (context) {
const options = context.options[0]
const caseType = ['camelCase', 'kebab-case', 'PascalCase'].indexOf(options) !== -1 ? options : 'PascalCase'
Copy link
Member

Choose a reason for hiding this comment

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

Please move the array with possible caseTypes out of the create function, it's how it's done in most rules I think.


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

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

Choose a reason for hiding this comment

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

Please wrap this line to make it more readable

if (node) {
Copy link
Member

Choose a reason for hiding this comment

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

Add empty line above if

Copy link
Member

Choose a reason for hiding this comment

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

Btw. you can also do:

if (!node) { return; }

To not introduce unnecessary indentation.

const value = convertCase(node.value.value, caseType)
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: 'Name property casing for consistency purposes',
category: 'Stylistic Issues',
recommended: false
},
fixable: 'code', // or "code" or "whitespace"
schema: [
{
enum: ['camelCase', 'kebab-case', 'PascalCase']
}
]
},

create
}
125 changes: 125 additions & 0 deletions tests/lib/rules/name-property-casing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @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
}]
}
]
})