diff --git a/lib/utils/casing.js b/lib/utils/casing.js index 741481ef8..719e80544 100644 --- a/lib/utils/casing.js +++ b/lib/utils/casing.js @@ -1,6 +1,25 @@ const assert = require('assert') -const invalidChars = /[^a-zA-Z0-9:]+/g +function parseWords (str) { + return str + .normalize() + .replace(/[\-!#$%^&*()_+~`"'<>,./?\[\]{}\s\r\n\v\t]+/gu, ' ') + .split(/([A-Z\s][^A-Z\s]*)/gu) + .map(word => word.trim()) + .filter(Boolean) +} + +function toLowerCase (str) { + return str.replace(/[A-Z]/gu, (l) => l.toLowerCase()) +} + +function toUpperCase (str) { + return str.replace(/[a-z]/gu, (l) => l.toUpperCase()) +} + +function capitalizeFirstLetter (str) { + return toUpperCase(str[0]) + str.slice(1) +} /** * Convert text to kebab-case @@ -8,12 +27,7 @@ const invalidChars = /[^a-zA-Z0-9:]+/g * @return {string} */ function kebabCase (str) { - return str - .replace(/[A-Z]/g, match => '-' + match) - .replace(/([^a-zA-Z])-([A-Z])/g, match => match[0] + match[2]) - .replace(/^-/, '') - .replace(invalidChars, '-') - .toLowerCase() + return toLowerCase(parseWords(str).join('-')) } /** @@ -22,12 +36,7 @@ function kebabCase (str) { * @return {string} */ function snakeCase (str) { - return str - .replace(/[A-Z]/g, match => '_' + match) - .replace(/([^a-zA-Z])_([A-Z])/g, match => match[0] + match[2]) - .replace(/^_/, '') - .replace(invalidChars, '_') - .toLowerCase() + return toLowerCase(parseWords(str).join('_')) } /** @@ -36,12 +45,9 @@ function snakeCase (str) { * @return {string} Converted string */ function camelCase (str) { - return str - .replace(/_/g, (_, index) => index === 0 ? _ : '-') - .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => - index === 0 ? letter.toLowerCase() : letter.toUpperCase() - ) - .replace(invalidChars, '') + return parseWords(str) + .map((word, index) => index === 0 ? toLowerCase(word) : capitalizeFirstLetter(word)) + .join('') } /** @@ -50,10 +56,9 @@ function camelCase (str) { * @return {string} Converted string */ function pascalCase (str) { - return str - .replace(/_/g, (_, index) => index === 0 ? _ : '-') - .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => letter.toUpperCase()) - .replace(invalidChars, '') + return parseWords(str) + .map((word) => capitalizeFirstLetter(word)) + .join('') } const convertersMap = { diff --git a/tests/lib/rules/prop-name-casing.js b/tests/lib/rules/prop-name-casing.js index 842e07ab8..1dd961079 100644 --- a/tests/lib/rules/prop-name-casing.js +++ b/tests/lib/rules/prop-name-casing.js @@ -287,6 +287,30 @@ ruleTester.run('prop-name-casing', rule, { } `, parserOptions + }, + { + // Japanese characters + filename: 'test.vue', + code: ` + export default { + props: { + '漢字': String + } + } + `, + parserOptions + }, + { + // emoji + filename: 'test.vue', + code: ` + export default { + props: { + '\u{1F37B}': String + } + } + `, + parserOptions } ], @@ -489,42 +513,6 @@ ruleTester.run('prop-name-casing', rule, { line: 4 }] }, - { - // emoji - filename: 'test.vue', - code: ` - export default { - props: { - '\u{1F37B}': String - } - } - `, - output: null, - parserOptions, - errors: [{ - message: 'Prop "\u{1F37B}" is not in camelCase.', - type: 'Property', - line: 4 - }] - }, - { - // Japanese characters - filename: 'test.vue', - code: ` - export default { - props: { - '漢字': String - } - } - `, - output: null, - parserOptions, - errors: [{ - message: 'Prop "漢字" is not in camelCase.', - type: 'Property', - line: 4 - }] - }, { filename: 'test.vue', code: ` diff --git a/tests/lib/utils/casing.js b/tests/lib/utils/casing.js index d72d186d5..81014d6eb 100644 --- a/tests/lib/utils/casing.js +++ b/tests/lib/utils/casing.js @@ -9,6 +9,7 @@ describe('getConverter()', () => { it('should convert string to camelCase', () => { const converter = casing.getConverter('camelCase') + assert.equal(converter('foo'), 'foo') assert.equal(converter('fooBar'), 'fooBar') assert.equal(converter('foo-bar'), 'fooBar') assert.equal(converter('foo_bar'), 'fooBar') @@ -17,11 +18,18 @@ describe('getConverter()', () => { assert.equal(converter('FooBAR'), 'fooBAR') assert.equal(converter('Foo1BAZ'), 'foo1BAZ') assert.equal(converter('foo1b_a_z'), 'foo1bAZ') + assert.equal(converter('darИībaÊÊw'), 'darИībaÊÊw') + assert.equal(converter('klâwen-ûf'), 'klâwenûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхИвдалП') + assert.equal(converter('kpłĄżć'), 'kpłĄżć') + assert.equal(converter('ÊtreSîne'), 'ÊtreSîne') + assert.equal(converter(' foo Bar '), 'fooBar') }) it('should convert string to PascalCase', () => { const converter = casing.getConverter('PascalCase') + assert.equal(converter('foo'), 'Foo') assert.equal(converter('fooBar'), 'FooBar') assert.equal(converter('foo-bar'), 'FooBar') assert.equal(converter('foo_bar'), 'FooBar') @@ -30,30 +38,50 @@ describe('getConverter()', () => { assert.equal(converter('FooBAR'), 'FooBAR') assert.equal(converter('Foo1BAZ'), 'Foo1BAZ') assert.equal(converter('foo1b_a_z'), 'Foo1bAZ') + assert.equal(converter('darИībaÊÊw'), 'DarИībaÊÊw') + assert.equal(converter('klâwen-ûf'), 'Klâwenûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхИвдалП') + assert.equal(converter('kpłĄżć'), 'KpłĄżć') + assert.equal(converter('ÊtreSîne'), 'ÊtreSîne') + assert.equal(converter(' foo Bar '), 'FooBar') }) it('should convert string to kebab-case', () => { const converter = casing.getConverter('kebab-case') + assert.equal(converter('foo'), 'foo') assert.equal(converter('fooBar'), 'foo-bar') assert.equal(converter('foo-bar'), 'foo-bar') assert.equal(converter('foo_bar'), 'foo-bar') assert.equal(converter('FooBar'), 'foo-bar') - assert.equal(converter('Foo1Bar'), 'foo1bar') + assert.equal(converter('Foo1Bar'), 'foo1-bar') assert.equal(converter('FooBAR'), 'foo-b-a-r') - assert.equal(converter('Foo1BAZ'), 'foo1b-a-z') + assert.equal(converter('Foo1BAZ'), 'foo1-b-a-z') assert.equal(converter('foo1b_a_z'), 'foo1b-a-z') + assert.equal(converter('darИībaÊÊw'), 'darИībaÊÊw') + assert.equal(converter('klâwen-ûf'), 'klâwen-ûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхИвдалП') + assert.equal(converter('kpłĄżć'), 'kpłĄżć') + assert.equal(converter('ÊtreSîne'), 'Être-sîne') + assert.equal(converter(' foo Bar '), 'foo-bar') }) it('should convert string to snake_case', () => { const converter = casing.getConverter('snake_case') + assert.equal(converter('a'), 'a') assert.equal(converter('fooBar'), 'foo_bar') assert.equal(converter('foo-bar'), 'foo_bar') assert.equal(converter('FooBar'), 'foo_bar') - assert.equal(converter('Foo1Bar'), 'foo1bar') + assert.equal(converter('Foo1Bar'), 'foo1_bar') assert.equal(converter('FooBAR'), 'foo_b_a_r') - assert.equal(converter('Foo1BAZ'), 'foo1b_a_z') + assert.equal(converter('Foo1BAZ'), 'foo1_b_a_z') assert.equal(converter('foo1b_a_z'), 'foo1b_a_z') + assert.equal(converter('darИībaÊÊw'), 'darИībaÊÊw') + assert.equal(converter('klâwen-ûf'), 'klâwen_ûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхИвдалП') + assert.equal(converter('kpłĄżć'), 'kpłĄżć') + assert.equal(converter('ÊtreSîne'), 'Être_sîne') + assert.equal(converter(' foo Bar '), 'foo_bar') }) })