diff --git a/src/compiler/parser/html-parser.js b/src/compiler/parser/html-parser.js index e2203613204..996178245ff 100644 --- a/src/compiler/parser/html-parser.js +++ b/src/compiler/parser/html-parser.js @@ -14,9 +14,10 @@ import { isNonPhrasingTag } from 'web/compiler/util' // Regular Expressions for parsing tags and attributes const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -const ncname = '[a-zA-Z_][\\w\\-\\.]*' +// use https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname +// except \u10000-\uEFFFF because of performance problem +export const pcenchars = '[\\-\\.0-9_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]' +const ncname = `[a-zA-Z_]${pcenchars}*` const qnameCapture = `((?:${ncname}\\:)?${ncname})` const startTagOpen = new RegExp(`^<${qnameCapture}`) const startTagClose = /^\s*(\/?)>/ diff --git a/src/core/util/options.js b/src/core/util/options.js index 5f0d10d3361..b85269fe4a0 100644 --- a/src/core/util/options.js +++ b/src/core/util/options.js @@ -4,6 +4,7 @@ import config from '../config' import { warn } from './debug' import { nativeWatch } from './env' import { set } from '../observer/index' +import { pcenchars } from '../../compiler/parser/html-parser' import { ASSET_TYPES, @@ -253,11 +254,10 @@ function checkComponents (options: Object) { } export function validateComponentName (name: string) { - if (!/^[a-zA-Z][\w-]*$/.test(name)) { + if (!new RegExp(`^[a-zA-Z]${pcenchars}*$`).test(name)) { warn( 'Invalid component name: "' + name + '". Component names ' + - 'can only contain alphanumeric characters and the hyphen, ' + - 'and must start with a letter.' + 'should conform to valid custom element name in html5 specification.' ) } if (isBuiltInTag(name) || config.isReservedTag(name)) { diff --git a/test/unit/features/options/name.spec.js b/test/unit/features/options/name.spec.js index 2f586f85893..051ea403064 100644 --- a/test/unit/features/options/name.spec.js +++ b/test/unit/features/options/name.spec.js @@ -15,7 +15,7 @@ describe('Options name', () => { }) /* eslint-disable */ - expect(`Invalid component name: "Hyper*Vue". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`) + expect(`Invalid component name: "Hyper*Vue".`) .toHaveBeenWarned() /* eslint-enable */ @@ -24,7 +24,7 @@ describe('Options name', () => { }) /* eslint-disable */ - expect(`Invalid component name: "2Cool2BValid". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`) + expect(`Invalid component name: "2Cool2BValid".`) .toHaveBeenWarned() /* eslint-enable */ }) @@ -37,4 +37,12 @@ describe('Options name', () => { expect(SuperComponent.options.components['SuperVue']).toEqual(SuperComponent) expect(SuperComponent.options.components['super-component']).toEqual(SuperComponent) }) + + it('should allow all potential custom element name for component name including non-alphanumeric characters', () => { + Vue.extend({ + name: 'my-컴포넌트' + }) + + expect(`Invalid component name`).not.toHaveBeenWarned() + }) })