├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE-MIT.txt ├── README.md ├── build.js ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [{README.md,package.json,.travis.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Installed npm modules 2 | node_modules 3 | 4 | # Folder view configuration files 5 | .DS_Store 6 | Desktop.ini 7 | 8 | # Thumbnail cache files 9 | ._* 10 | Thumbs.db 11 | 12 | # Files that might appear on external disks 13 | .Spotlight-V100 14 | .Trashes 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "14" 4 | git: 5 | depth: 1 6 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | Copyright Mathias Bynens 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # is-potential-custom-element-name [![Build status](https://travis-ci.org/mathiasbynens/is-potential-custom-element-name.svg?branch=master)](https://travis-ci.org/mathiasbynens/is-potential-custom-element-name) 2 | 3 | _is-potential-custom-element-name_ checks whether a given string matches [the `PotentialCustomElementName` production](https://html.spec.whatwg.org/multipage/scripting.html#prod-potentialcustomelementname) as defined in the HTML Standard. 4 | 5 | ## Installation 6 | 7 | To use _is-potential-custom-element-name_ programmatically, install it as a dependency via [npm](https://www.npmjs.com/): 8 | 9 | ```bash 10 | $ npm install is-potential-custom-element-name 11 | ``` 12 | 13 | Then, `require` it: 14 | 15 | ```js 16 | const isPotentialCustomElementName = require('is-potential-custom-element-name'); 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```js 22 | isPotentialCustomElementName('foo-bar'); 23 | // → true 24 | isPotentialCustomElementName('Foo-bar'); 25 | // → false 26 | isPotentialCustomElementName('baz-©'); 27 | // → false 28 | isPotentialCustomElementName('annotation-xml'); 29 | // → true 30 | ``` 31 | 32 | ## Author 33 | 34 | | [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | 35 | |---| 36 | | [Mathias Bynens](https://mathiasbynens.be/) | 37 | 38 | ## License 39 | 40 | _is-potential-custom-element-name_ is available under the [MIT](https://mths.be/mit) license. 41 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const regenerate = require('regenerate'); 5 | 6 | // https://html.spec.whatwg.org/multipage/scripting.html#valid-custom-element-name 7 | 8 | // PCENChar ::= 9 | // "-" | "." | [0-9] | "_" | [a-z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] 10 | const set = regenerate('-', '.', '_', 0x00B7) 11 | .addRange('0', '9') 12 | .addRange('a', 'z') 13 | .addRange(0x00C0, 0x00D6) 14 | .addRange(0x00D8, 0x00F6) 15 | .addRange(0x00F8, 0x037D) 16 | .addRange(0x037F, 0x1FFF) 17 | .addRange(0x200C, 0x200D) 18 | .addRange(0x203F, 0x2040) 19 | .addRange(0x2070, 0x218F) 20 | .addRange(0x2C00, 0x2FEF) 21 | .addRange(0x3001, 0xD7FF) 22 | .addRange(0xF900, 0xFDCF) 23 | .addRange(0xFDF0, 0xFFFD) 24 | .addRange(0x010000, 0x0EFFFF); 25 | const PCENChar = set.toString(); 26 | const PCENCharNoHyphen = set.clone().remove('-').toString(); 27 | 28 | // PotentialCustomElementName ::= 29 | // [a-z] (PCENChar)* '-' (PCENChar)* 30 | // Note: The hyphen is omitted from the first group to avoid excess backtracking. 31 | const PotentialCustomElementName = `[a-z](?:${ PCENCharNoHyphen })*-(?:${ PCENChar })*`; 32 | const source = `// Generated using \`npm run build\`. Do not edit. 33 | 34 | var regex = /^${ PotentialCustomElementName }$/; 35 | 36 | var isPotentialCustomElementName = function(string) { 37 | return regex.test(string); 38 | }; 39 | 40 | module.exports = isPotentialCustomElementName; 41 | `; 42 | fs.writeFileSync('index.js', source); 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Generated using `npm run build`. Do not edit. 2 | 3 | var regex = /^[a-z](?:[\.0-9_a-z\xB7\xC0-\xD6\xD8-\xF6\xF8-\u037D\u037F-\u1FFF\u200C\u200D\u203F\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]|[\uD800-\uDB7F][\uDC00-\uDFFF])*-(?:[\x2D\.0-9_a-z\xB7\xC0-\xD6\xD8-\xF6\xF8-\u037D\u037F-\u1FFF\u200C\u200D\u203F\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]|[\uD800-\uDB7F][\uDC00-\uDFFF])*$/; 4 | 5 | var isPotentialCustomElementName = function(string) { 6 | return regex.test(string); 7 | }; 8 | 9 | module.exports = isPotentialCustomElementName; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "is-potential-custom-element-name", 3 | "version": "1.0.1", 4 | "description": "Check whether a given string matches the `PotentialCustomElementName` production as defined in the HTML Standard.", 5 | "homepage": "https://github.com/mathiasbynens/is-potential-custom-element-name", 6 | "main": "index.js", 7 | "files": [ 8 | "LICENSE-MIT.txt", 9 | "index.js" 10 | ], 11 | "keywords": [ 12 | "html", 13 | "custom element", 14 | "custom element name", 15 | "web components" 16 | ], 17 | "license": "MIT", 18 | "author": { 19 | "name": "Mathias Bynens", 20 | "url": "https://mathiasbynens.be/" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/mathiasbynens/is-potential-custom-element-name.git" 25 | }, 26 | "bugs": "https://github.com/mathiasbynens/is-potential-custom-element-name/issues", 27 | "devDependencies": { 28 | "mocha": "^2.2.1", 29 | "regenerate": "^1.4.2" 30 | }, 31 | "scripts": { 32 | "build": "node build.js", 33 | "test": "mocha" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var isPotentialCustomElementName = require('./index.js'); 3 | 4 | describe('is-potential-custom-element-name', function() { 5 | it('returns a boolean indicating whether the given string is a potential custom element name', function() { 6 | assert.equal(isPotentialCustomElementName('foo-bar'), true); 7 | assert.equal(isPotentialCustomElementName('Foo-bar'), false); 8 | assert.equal(isPotentialCustomElementName('baz-©'), false); 9 | assert.equal(isPotentialCustomElementName('annotation-xml'), true); 10 | }); 11 | 12 | it('avoids excess backtracking', function() { 13 | const longA = 'a'.repeat(20000000); 14 | assert.strictEqual(isPotentialCustomElementName('a-'.concat(longA)), true); 15 | }) 16 | }); 17 | --------------------------------------------------------------------------------