Skip to content

Commit

Permalink
feat(@angular-devkit/schematics): support unicode character HTML elem…
Browse files Browse the repository at this point in the history
…ent names (#13837)

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.
  • Loading branch information
clydin authored and vikerman committed Mar 8, 2019
1 parent 324d9f2 commit b147742
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 9 deletions.
39 changes: 34 additions & 5 deletions packages/angular_devkit/schematics/src/formats/html-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -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' };
Expand Down Expand Up @@ -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);
});
});

0 comments on commit b147742

Please sign in to comment.