From 81e80a6662bda0f2b666ea613fe4fc48529a0df7 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 6 Mar 2019 13:52:47 -0500 Subject: [PATCH] feat(@angular-devkit/schematics): support unicode character HTML element names The HTML specification allows for a wide variety of characters to be present within a custom element name. The previous behavior limited the names to mostly alphanumeric characters. This change opens up the names to include the characters specified within the specification for custom element names. --- .../schematics/src/formats/html-selector.ts | 39 ++++++++++++++++--- .../src/formats/html-selector_spec.ts | 17 ++++++-- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/angular_devkit/schematics/src/formats/html-selector.ts b/packages/angular_devkit/schematics/src/formats/html-selector.ts index 6d7d9ada9b0b..b048482d2eb7 100644 --- a/packages/angular_devkit/schematics/src/formats/html-selector.ts +++ b/packages/angular_devkit/schematics/src/formats/html-selector.ts @@ -5,18 +5,47 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - import { schema } from '@angular-devkit/core'; +// As per https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name +// * Without mandatory `-` as the application prefix will generally cover its inclusion +// * And an allowance for upper alpha characters + +// NOTE: This should eventually be broken out into two formats: full and partial (allows for prefix) + +const unicodeRanges = [ + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x37D], + [0x37F, 0x1FFF], + [0x200C, 0x200D], + [0x203F, 0x2040], + [0x2070, 0x218F], + [0x2C00, 0x2FEF], + [0x3001, 0xD7FF], + [0xF900, 0xFDCF], + [0xFDF0, 0xFFFD], + [0x10000, 0xEFFFF], +]; + +function isValidElementName(name: string) { + let regex = '^[a-zA-Z]['; + + regex += '-.0-9_a-zA-Z\\u{B7}'; + + for (const range of unicodeRanges) { + regex += `\\u{${range[0].toString(16)}}-\\u{${range[1].toString(16)}}`; + } + + regex += ']*$'; -// Must start with a letter, and must contain only alphanumeric characters or dashes. -// When adding a dash the segment after the dash must also start with a letter. -export const htmlSelectorRe = /^[a-zA-Z][.0-9a-zA-Z]*(:?-[a-zA-Z][.0-9a-zA-Z]*)*$/; + return new RegExp(regex, 'u').test(name); +} export const htmlSelectorFormat: schema.SchemaFormat = { name: 'html-selector', formatter: { async: false, - validate: (selector: string) => htmlSelectorRe.test(selector), + validate: name => typeof name === 'string' && isValidElementName(name), }, }; diff --git a/packages/angular_devkit/schematics/src/formats/html-selector_spec.ts b/packages/angular_devkit/schematics/src/formats/html-selector_spec.ts index c59ad62d6641..411d1df67df9 100644 --- a/packages/angular_devkit/schematics/src/formats/html-selector_spec.ts +++ b/packages/angular_devkit/schematics/src/formats/html-selector_spec.ts @@ -5,12 +5,10 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - import { map } from 'rxjs/operators'; import { formatValidator } from './format-validator'; import { htmlSelectorFormat } from './html-selector'; - describe('Schematics HTML selector format', () => { it('accepts correct selectors', done => { const data = { selector: 'my-selector' }; @@ -45,14 +43,25 @@ describe('Schematics HTML selector format', () => { .toPromise().then(done, done.fail); }); - it('rejects selectors with non-letter after dash', done => { + it('accepts selectors with non-letter after dash', done => { const data = { selector: 'my-1selector' }; const dataSchema = { properties: { selector: { type: 'string', format: 'html-selector' } }, }; formatValidator(data, dataSchema, [htmlSelectorFormat]) - .pipe(map(result => expect(result.success).toBe(false))) + .pipe(map(result => expect(result.success).toBe(true))) + .toPromise().then(done, done.fail); + }); + + it('accepts selectors with unicode', done => { + const data = { selector: 'app-root😀' }; + const dataSchema = { + properties: { selector: { type: 'string', format: 'html-selector' } }, + }; + + formatValidator(data, dataSchema, [htmlSelectorFormat]) + .pipe(map(result => expect(result.success).toBe(true))) .toPromise().then(done, done.fail); }); });