Skip to content

Commit 9c71852

Browse files
youngrokyyx990803
authored andcommitted
feat(compiler/watch): allow unicode characters in component names and watch paths (#8666)
close #8564
1 parent 325fc76 commit 9c71852

File tree

5 files changed

+36
-11
lines changed

5 files changed

+36
-11
lines changed

src/compiler/parser/html-parser.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111

1212
import { makeMap, no } from 'shared/util'
1313
import { isNonPhrasingTag } from 'web/compiler/util'
14+
import { unicodeLetters } from 'core/util/lang'
1415

1516
// Regular Expressions for parsing tags and attributes
1617
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
17-
// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
18-
// but for Vue templates we can enforce a simple charset
19-
const ncname = '[a-zA-Z_][\\w\\-\\.]*'
18+
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeLetters}]*`
2019
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
2120
const startTagOpen = new RegExp(`^<${qnameCapture}`)
2221
const startTagClose = /^\s*(\/?)>/

src/core/util/lang.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
/* @flow */
22

3+
/**
4+
* unicode letters used for parsing html tags, component names and property paths.
5+
* using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname
6+
* skipping \u10000-\uEFFFF due to it freezing up PhantomJS
7+
*/
8+
export const unicodeLetters = 'a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD'
9+
310
/**
411
* Check if a string starts with $ or _
512
*/
@@ -23,7 +30,7 @@ export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
2330
/**
2431
* Parse simple path.
2532
*/
26-
const bailRE = /[^\w.$]/
33+
const bailRE = new RegExp(`[^${unicodeLetters}.$]`)
2734
export function parsePath (path: string): any {
2835
if (bailRE.test(path)) {
2936
return

src/core/util/options.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import config from '../config'
44
import { warn } from './debug'
5-
import { nativeWatch } from './env'
65
import { set } from '../observer/index'
7-
import { hasSymbol } from '../util/index'
6+
import { unicodeLetters } from './lang'
7+
import { nativeWatch, hasSymbol } from './env'
88

99
import {
1010
ASSET_TYPES,
@@ -264,11 +264,10 @@ function checkComponents (options: Object) {
264264
}
265265

266266
export function validateComponentName (name: string) {
267-
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
267+
if (!new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeLetters}]*$`).test(name)) {
268268
warn(
269269
'Invalid component name: "' + name + '". Component names ' +
270-
'can only contain alphanumeric characters and the hyphen, ' +
271-
'and must start with a letter.'
270+
'should conform to valid custom element name in html5 specification.'
272271
)
273272
}
274273
if (isBuiltInTag(name) || config.isReservedTag(name)) {

test/unit/features/instance/methods-data.spec.js

+12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ describe('Instance methods data', () => {
2626
data: {
2727
a: {
2828
b: 1
29+
},
30+
유니코드: {
31+
なまえ: 'ok'
2932
}
3033
},
3134
methods: {
@@ -108,6 +111,15 @@ describe('Instance methods data', () => {
108111
expect(spy).toHaveBeenCalledWith(1)
109112
})
110113

114+
it('handler option in string', () => {
115+
vm.$watch('유니코드.なまえ', {
116+
handler: 'foo',
117+
immediate: true
118+
})
119+
expect(spy.calls.count()).toBe(1)
120+
expect(spy).toHaveBeenCalledWith('ok')
121+
})
122+
111123
it('warn expression', () => {
112124
vm.$watch('a + b', spy)
113125
expect('Watcher only accepts simple dot-delimited paths').toHaveBeenWarned()

test/unit/features/options/name.spec.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('Options name', () => {
1515
})
1616

1717
/* eslint-disable */
18-
expect(`Invalid component name: "Hyper*Vue". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`)
18+
expect(`Invalid component name: "Hyper*Vue".`)
1919
.toHaveBeenWarned()
2020
/* eslint-enable */
2121

@@ -24,7 +24,7 @@ describe('Options name', () => {
2424
})
2525

2626
/* eslint-disable */
27-
expect(`Invalid component name: "2Cool2BValid". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`)
27+
expect(`Invalid component name: "2Cool2BValid".`)
2828
.toHaveBeenWarned()
2929
/* eslint-enable */
3030
})
@@ -37,4 +37,12 @@ describe('Options name', () => {
3737
expect(SuperComponent.options.components['SuperVue']).toEqual(SuperComponent)
3838
expect(SuperComponent.options.components['super-component']).toEqual(SuperComponent)
3939
})
40+
41+
it('should allow all potential custom element name for component name including non-alphanumeric characters', () => {
42+
Vue.extend({
43+
name: 'my-컴포넌트'
44+
})
45+
46+
expect(`Invalid component name`).not.toHaveBeenWarned()
47+
})
4048
})

0 commit comments

Comments
 (0)