Skip to content

Fix: casing of unicode characters #694

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

Closed
wants to merge 6 commits into from
Closed
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
51 changes: 28 additions & 23 deletions lib/utils/casing.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
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
* @param {string} str Text to be converted
* @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('-'))
}

/**
Expand All @@ -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('_'))
}

/**
Expand All @@ -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('')
}

/**
Expand All @@ -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 = {
Expand Down
60 changes: 24 additions & 36 deletions tests/lib/rules/prop-name-casing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
],

Expand Down Expand Up @@ -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: `
Expand Down
36 changes: 32 additions & 4 deletions tests/lib/utils/casing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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')
Expand All @@ -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')
})
})