Repository: vuejs/eslint-plugin-vue
Pull request #106 (2017-07-23 20:26:53)

Allow to use numbers in component names
Currently if there is any `number` in component name it will trigger error.

I decided to move casing functions to separate file because there is going to ba at-least 2-3 more rules witch are going to use same behavior, and i was able to write tests for it.


Patch:
diff --git a/lib/rules/name-property-casing.js b/lib/rules/name-property-casing.js
index ace94908..c4136d85 100644
--- a/lib/rules/name-property-casing.js
+++ b/lib/rules/name-property-casing.js
@@ -5,43 +5,7 @@
 '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
-}
+const casing = require('../utils/casing')
 
 // ------------------------------------------------------------------------------
 // Rule Definition
@@ -49,7 +13,7 @@ function getConverter (name) {
 
 function create (context) {
   const options = context.options[0]
-  const caseType = allowedCaseOptions.indexOf(options) !== -1 ? options : 'PascalCase'
+  const caseType = casing.allowedCaseOptions.indexOf(options) !== -1 ? options : 'PascalCase'
 
   // ----------------------------------------------------------------------
   // Public
@@ -65,7 +29,7 @@ function create (context) {
 
     if (!node) return
 
-    const value = getConverter(caseType)(node.value.value)
+    const value = casing.getConverter(caseType)(node.value.value)
     if (value !== node.value.value) {
       context.report({
         node: node.value,
@@ -90,7 +54,7 @@ module.exports = {
     fixable: 'code',  // or "code" or "whitespace"
     schema: [
       {
-        enum: allowedCaseOptions
+        enum: casing.allowedCaseOptions
       }
     ]
   },
diff --git a/lib/utils/casing.js b/lib/utils/casing.js
new file mode 100644
index 00000000..15fd415a
--- /dev/null
+++ b/lib/utils/casing.js
@@ -0,0 +1,44 @@
+const assert = require('assert')
+
+const invalidChars = /[^a-zA-Z0-9:]+/g
+
+function kebabCase (str) {
+  return str
+    .replace(/([a-z])([A-Z])/g, match => match[0] + '-' + match[1])
+    .replace(invalidChars, '-')
+    .toLowerCase()
+}
+
+function camelCase (str) {
+  return str
+    .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => (
+      index === 0 ? letter.toLowerCase() : letter.toUpperCase())
+    )
+    .replace(invalidChars, '')
+}
+
+function pascalCase (str) {
+  return str
+    .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => letter.toUpperCase())
+    .replace(invalidChars, '')
+}
+
+const convertersMap = {
+  'kebab-case': kebabCase,
+  'camelCase': camelCase,
+  'PascalCase': pascalCase
+}
+
+module.exports = {
+  allowedCaseOptions: [
+    'camelCase',
+    'kebab-case',
+    'PascalCase'
+  ],
+
+  getConverter (name) {
+    assert(typeof name === 'string')
+
+    return convertersMap[name] || pascalCase
+  }
+}


Test patch:
diff --git a/tests/lib/utils/casing.js b/tests/lib/utils/casing.js
new file mode 100644
index 00000000..743d24fd
--- /dev/null
+++ b/tests/lib/utils/casing.js
@@ -0,0 +1,35 @@
+'use strict'
+
+const casing = require('../../../lib/utils/casing')
+const chai = require('chai')
+
+const assert = chai.assert
+
+describe('getConverter()', () => {
+  it('should conver string to camelCase', () => {
+    const converter = casing.getConverter('camelCase')
+
+    assert.ok(converter('fooBar') === 'fooBar')
+    assert.ok(converter('foo-bar') === 'fooBar')
+    assert.ok(converter('FooBar') === 'fooBar')
+    assert.ok(converter('Foo1Bar') === 'foo1Bar')
+  })
+
+  it('should conver string to PascalCase', () => {
+    const converter = casing.getConverter('PascalCase')
+
+    assert.ok(converter('fooBar') === 'FooBar')
+    assert.ok(converter('foo-bar') === 'FooBar')
+    assert.ok(converter('FooBar') === 'FooBar')
+    assert.ok(converter('Foo1Bar') === 'Foo1Bar')
+  })
+
+  it('should conver string to kebab-case', () => {
+    const converter = casing.getConverter('kebab-case')
+
+    assert.ok(converter('fooBar') === 'foo-bar')
+    assert.ok(converter('foo-bar') === 'foo-bar')
+    assert.ok(converter('FooBar') === 'foo-bar')
+    assert.ok(converter('Foo1Bar') === 'foo1bar')
+  })
+})