├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── LICENSE ├── NOTICE ├── README.md ├── css-property-parser.d.ts ├── package-lock.json ├── package.json ├── src ├── constants │ ├── css.js │ ├── genericLexer.js │ ├── grammars.js │ ├── index.js │ ├── paths.js │ ├── shorthandIdentToLonghandPropertyMap.json │ ├── shorthandProperties.js │ └── syntaxOverrides.json ├── errors │ ├── ParseError.js │ ├── UnknownPropertyError.js │ ├── UnsupportedPropertyError.js │ └── index.js ├── expandShorthandProperty.js ├── formatters │ ├── grammarFormatters │ │ ├── JsonGrammarFormatter.js │ │ └── NearleyGrammarFormatter.js │ └── shorthandPropertyTypeFormatters │ │ ├── BackgroundPropertyFormatter.js │ │ ├── BorderRadiusPropertyFormatter.js │ │ ├── CommaSeparatedListPropertyFormatter.js │ │ ├── FlexPropertyFormatter.js │ │ ├── FontPropertyFormatter.js │ │ ├── TrblPropertyFormatter.js │ │ ├── UnorderedOptionalListPropertyFormatter.js │ │ └── index.js ├── getShorthandComputedProperties.js ├── getShorthandsForProperty.js ├── grammars │ ├── LICENSE │ ├── js │ │ └── formalSyntax.js │ ├── json │ │ ├── an-plus-b.json │ │ ├── angle.json │ │ ├── any-value.json │ │ ├── attr-fallback.json │ │ ├── attr-name.json │ │ ├── background.json │ │ ├── color.json │ │ ├── cubic-bezier-timing-function.json │ │ ├── custom-ident.json │ │ ├── custom-property-name.json │ │ ├── declaration-list.json │ │ ├── declaration-value.json │ │ ├── declaration.json │ │ ├── dimension.json │ │ ├── feature-value-declaration.json │ │ ├── final-bg-layer.json │ │ ├── flex.json │ │ ├── frames-timing-function.json │ │ ├── frequency.json │ │ ├── function-token.json │ │ ├── hex-color.json │ │ ├── hsl().json │ │ ├── hsla().json │ │ ├── id-selector.json │ │ ├── ident.json │ │ ├── image().json │ │ ├── integer.json │ │ ├── length.json │ │ ├── name-repeat.json │ │ ├── number.json │ │ ├── outline-radius.json │ │ ├── path().json │ │ ├── percentage.json │ │ ├── positive-integer.json │ │ ├── ratio.json │ │ ├── resolution.json │ │ ├── rgb().json │ │ ├── rgba().json │ │ ├── shape.json │ │ ├── string.json │ │ ├── time.json │ │ ├── url.json │ │ ├── x.json │ │ └── y.json │ └── nearley │ │ ├── formalSyntax.ne │ │ └── properties │ │ ├── animation.ne │ │ ├── background.ne │ │ ├── font.ne │ │ └── transition.ne ├── index.js ├── initialValueMap.js ├── isInitialValue.js ├── isShorthandProperty.js ├── isValidDeclaration.js ├── scripts │ ├── .eslintrc.json │ ├── extractProperties.js │ ├── formatData.js │ ├── formatFormalSyntaxes.js │ ├── formatGrammars.js │ └── updateBasicDataUnits.js └── utils │ ├── ArrayUtils.js │ ├── CaseConverterUtils.js │ ├── LocationIndexTracker.js │ ├── ShorthandPropertyClassifierUtils.js │ └── ShorthandPropertyTypeFormatterUtils.js ├── test ├── .eslintrc.json ├── BaseGrammarsTest.js ├── ExpandShorthandPropertyTest.js ├── GetShorthandComputedPropertiesTest.js ├── GetShorthandsForPropertyTest.js ├── InitialValuesTest.js ├── benchmark.js ├── formatters │ ├── JsonGrammarFormatterTest.js │ └── NearleyGrammarFormatterTest.js └── isValidDeclarationTest.js └── updateCSSData.sh /.eslintignore: -------------------------------------------------------------------------------- 1 | src/grammars/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 7 4 | }, 5 | "env": { 6 | "node": true 7 | }, 8 | "extends": "airbnb/base", 9 | "rules": { 10 | "max-len": [ 11 | "error", 12 | { 13 | "code": 120, 14 | "ignoreStrings": true, 15 | "ignoreRegExpLiterals": true 16 | } 17 | ], 18 | "no-bitwise": [ 19 | "error", 20 | { 21 | "allow": [ 22 | "~" 23 | ] 24 | } 25 | ], 26 | "no-underscore-dangle": "off", 27 | "no-shadow": "off", 28 | "comma-dangle": ["error", { 29 | "arrays": "always-multiline", 30 | "objects": "always-multiline", 31 | "imports": "always-multiline", 32 | "exports": "always-multiline", 33 | "functions": "ignore" 34 | }], 35 | "global-require": "off", 36 | "valid-jsdoc": "error" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .DS_STORE 4 | .vscode 5 | 6 | # ignore generated grammars 7 | src/formatted-data 8 | src/grammars/generated 9 | 10 | 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | src/grammars/json 3 | src/grammars/js 4 | src/grammars/nearley 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Mahir Shah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This software uses a derivative form of the data contained in 2 | https://github.com/mdn/data/blob/master/ (MDN Data). The software that is 3 | a derivative form of MDN Data, either checked in under src/grammar/ or 4 | distributed under src/grammar/generated, is licensed under MPL 2.0 5 | (https://www.mozilla.org/en-US/MPL/2.0/). 6 | 7 | All other software contained in this project is provided under the MIT license. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS Property Validation and Shorthand Expansion 2 | > Validate css properties and expand css shorthand properties 3 | 4 | ## Contents 5 | 6 | 7 | 8 | 9 | 10 | - [Why](#why) 11 | - [Installation](#installation) 12 | - [Usage](#usage) 13 | - [API](#api) 14 | - [isShorthandProperty(property: string): boolean](#isshorthandpropertyproperty-string-boolean) 15 | - [Examples](#examples) 16 | - [[Experimental] [isValidDeclaration(property: string, value: string): boolean](./src/isValidDeclaration.js)](#experimental-isvaliddeclarationproperty-string-value-string-booleansrcisvaliddeclarationjs) 17 | - [Examples](#examples-1) 18 | - [getShorthandComputedProperties(property: string, [recursivelyResolve=false]): Array](#getshorthandcomputedpropertiesproperty-string-recursivelyresolvefalse-array) 19 | - [Examples](#examples-2) 20 | - [expandShorthandProperty(property: string, value: string, [recursivelyResolve=false], [includeInitialValues=false]): Object](#expandshorthandpropertyproperty-string-value-string-recursivelyresolvefalse-includeinitialvaluesfalse-object) 21 | - [Examples](#examples-3) 22 | - [getShorthandsForProperty(property: string): Array<string>](#getshorthandsforpropertyproperty-string-arrayltstringgt) 23 | - [Examples](#examples-4) 24 | - [isInitialValue(property, value)](#isinitialvalueproperty-value) 25 | - [initialValues(property, recursivelyResolve, includeShorthands)](#initialvaluesproperty-recursivelyresolve-includeshorthands) 26 | - [Examples](#examples-5) 27 | - [initialValue(property)](#initialvalueproperty) 28 | - [Examples](#examples-6) 29 | - [Developer/Contribution HOWTO](#developercontribution-howto) 30 | 31 | 32 | 33 | 34 | ## Why 35 | 36 | - Uses [MDN data](https://github.com/mdn/data/tree/master/css) to generate validators and shorthand property expanders 37 | - Supports experimental properties and values 38 | 39 | ## Installation 40 | ``` 41 | $ npm install css-property-parser 42 | ``` 43 | 44 | ## Usage 45 | ```js 46 | const { 47 | isShorthandProperty, 48 | isValidDeclaration, 49 | getShorthandComputedProperties, 50 | expandShorthandProperty, 51 | getShorthandsForProperty, 52 | } = require('css-property-parser'); 53 | 54 | // isShorthandProperty 55 | // returns boolean indicating if the given property is a shorthand property 56 | console.log(isShorthandProperty('border')); // => true 57 | console.log(isShorthandProperty('color')); // => false 58 | 59 | // isValidDeclaration 60 | // returns boolean indicating if the given property value pair is valid 61 | console.log(isValidDeclaration('border', '1px solid black')); // => true 62 | console.log(isValidDeclaration('color', 'rgba(0, 0, 0, .25)')); // => true 63 | console.log(isValidDeclaration('z-index', 'abc')); // => false 64 | console.log(isValidDeclaration('height', 'red')); // => false 65 | 66 | // getShorthandComputedProperties 67 | // returns an array of computed property names for the given shorthand 68 | console.log(getShorthandComputedProperties('background')) 69 | // => [ 70 | // "background-image", 71 | // "background-position", 72 | // "background-size", 73 | // "background-repeat", 74 | // "background-origin", 75 | // "background-clip", 76 | // "background-attachment", 77 | // "background-color" 78 | // ] 79 | console.log(getShorthandComputedProperties('color')) 80 | // => ["color"] 81 | console.log(getShorthandComputedProperties('unknown')) 82 | // => [] 83 | 84 | // expandShorthandProperty 85 | // returns an obejct mapping longhand property names to their values 86 | console.log(expandShorthandProperty('margin', '0 3px 10rem')) 87 | // => { 88 | // 'margin-top': '0', 89 | // 'margin-right': '3px', 90 | // 'margin-bottom': '10rem', 91 | // 'margin-left': '3px', 92 | // } 93 | 94 | console.log(expandShorthandProperty('background', 'fixed padding-box url(image.png) rgb(255, 255, 0) 10px top / cover repeat-x')) 95 | // => { 96 | // 'background-attachment': 'fixed', 97 | // 'background-clip': 'padding-box', 98 | // 'background-origin': 'padding-box', 99 | // 'background-image': 'url(image.png)', 100 | // 'background-repeat': 'repeat-x', 101 | // 'background-color': 'rgb(255, 255, 0)', 102 | // 'background-position': '10px top', 103 | // 'background-size': 'cover', 104 | // } 105 | 106 | console.log(getShorthandsForProperty('border-left-width')); 107 | // => [ 'border-left-width', 'border-left', 'border-width', 'border' ] 108 | ``` 109 | 110 | ## API 111 | 112 | ### [isShorthandProperty(property: string): boolean](./src/isShorthandProperty.js) 113 | Checks if a given property is a shorthand property 114 | 115 | * property - the property name 116 | * returns true if property is a shorthand, false otherwise 117 | 118 | #### Examples 119 | 120 | ```js 121 | isShorthandProperty('border') 122 | // => true 123 | 124 | isShorthandProperty('color') 125 | // => false 126 | ``` 127 | 128 | ### [Experimental] [isValidDeclaration(property: string, value: string): boolean](./src/isValidDeclaration.js) 129 | Checks if the given property, value pair is valid. 130 | 131 | * property - the property name. For example, 'border' or 'color'. 132 | * value - the property value. For example, '1px solid black'. 133 | * returns true if the given value is valid for the property. Else, false. 134 | 135 | #### Examples 136 | 137 | ```js 138 | isValidDeclaration('color', 'currentColor') 139 | // => true 140 | 141 | isValidDeclaration('color', 'rgb(0)') 142 | // => false (rgba expects at least 3 parameters) 143 | 144 | isValidDeclaration('z-index', '-1') 145 | // => true 146 | 147 | isValidDeclaration('z-index', 'abc') 148 | // => false (z-index expects an integer) 149 | 150 | isValidDeclaration('width', '300px') 151 | // => true 152 | 153 | isValidDeclaration('width', '300ms') 154 | // => false ('ms' is not a valid length unit) 155 | ``` 156 | 157 | ### [getShorthandComputedProperties(property: string, [recursivelyResolve=false]): Array](./src/getShorthandComputedProperties.js) 158 | Given a shorthand property, returns an array of the computed properties for that shorthand property. If given 159 | a known property that is not a shorthand, simply returns the given property. If given an unknown property, 160 | returns an empty array. 161 | 162 | * shorthandProperty - the shorthand property name. For example, "background" or "border". 163 | * [recursivelyResolve=false] - recursively resolve additional longhand properties if the shorthands expand to additional shorthands. For example, the border property expands to border-width, which expands further to border-left-width, border-right-width, etc. 164 | * returns an array containing the computed properties for the given shorthand property. Returns an empty array if the given property is not a valid property. 165 | 166 | ##### Examples 167 | 168 | ```js 169 | getShorthandComputedProperties('background'); 170 | // -> [ 171 | // "background-image", 172 | // "background-position", 173 | // "background-size", 174 | // "background-repeat", 175 | // "background-origin", 176 | // "background-clip", 177 | // "background-attachment", 178 | // "background-color" 179 | // ] 180 | ``` 181 | 182 | ```js 183 | getShorthandComputedProperties('color'); 184 | // -> ["color"] 185 | ``` 186 | 187 | ```js 188 | getShorthandComputedProperties('border', true); 189 | // -> [ 190 | // 'border-width', 191 | // 'border-style', 192 | // 'border-color', 193 | // 'border-bottom-width', 194 | // 'border-left-width', 195 | // 'border-right-width', 196 | // 'border-top-width', 197 | // 'border-bottom-style', 198 | // 'border-left-style', 199 | // 'border-right-style', 200 | // 'border-top-style', 201 | // 'border-bottom-color', 202 | // 'border-left-color', 203 | // 'border-right-color', 204 | // 'border-top-color' 205 | // ]; 206 | ``` 207 | 208 | ```js 209 | getShorthandComputedProperties('unknownProperty'); 210 | // -> [] 211 | ``` 212 | 213 | ### [expandShorthandProperty(property: string, value: string, [recursivelyResolve=false], [includeInitialValues=false]): Object](./src/expandShorthandProperty.js) 214 | 215 | Given a property and value attempts to expand the value into its longhand equivalents. Returns an object 216 | mapping the property longhand names to the longhand values. If the property cannot be expanded (i.e. the property 217 | is not a shorthand property) simply returns an object mapping the original property to the original value. 218 | 219 | * propertyName - the property name for the given value 220 | * propertyValue - the value of the property 221 | * [recursivelyResolve=false] - recursively resolve additional longhand properties if the shorthands expand to additional shorthands. For example, the border property expands to border-width, which expands further to border-left-width, border-right-width, etc. 222 | * [includeInitialValues=false] - when expanding the shorthand property, fill in any missing longhand values with their initial value. For example, the property declaration "border: 1px" only explicitly sets the "border-width" longhand property. If this param is true, the returned object will fill in the initial values for "border-style" and "border-color". By default, the returned object will only contain the "border-width". 223 | * throws {[ParseError](./src/errors/ParseError.js)} - if the propertyValue cannot be parsed. 224 | * throws {[UnknownPropertyError](./src/errors/UnknownPropertyError.js)} - if the propertyName is not defined in mdn. 225 | * throws {[UnsupportedPropertyError](./src/errors/UnsupportedPropertyError.js)} - if the propertyName is a shorthand property, but we don't support expanding it yet. 226 | 227 | Currently supports the following properties: 228 | 229 | - animation 230 | - background 231 | - border 232 | - border-bottom 233 | - border-color 234 | - border-left 235 | - border-radius 236 | - border-right 237 | - border-style 238 | - border-top 239 | - border-width 240 | - column-rule 241 | - columns 242 | - flex 243 | - flex-flow 244 | - font 245 | - list-style 246 | - margin 247 | - outline 248 | - padding 249 | - text-decoration 250 | - text-emphasis 251 | - transition 252 | 253 | ##### Examples 254 | 255 | ```js 256 | expandShorthandProperty('margin', '0 3px 10rem') 257 | // => { 258 | // 'margin-top': '0', 259 | // 'margin-right': '3px', 260 | // 'margin-bottom': '10rem', 261 | // 'margin-left': '3px', 262 | // } 263 | ``` 264 | 265 | ```js 266 | expandShorthandProperty('flex', 'initial') 267 | // => { 268 | // 'flex-grow': 'initial', 269 | // 'flex-shrink': 'initial', 270 | // 'flex-basis': 'initial', 271 | // } 272 | ``` 273 | 274 | ```js 275 | expandShorthandProperty('border-radius', '10px 5px 2em / 20px 25px 30%') 276 | // => { 277 | // 'border-top-left-radius': '10px / 20px', 278 | // 'border-top-right-radius': '5px / 25px', 279 | // 'border-bottom-left-radius': '5px / 25px', 280 | // 'border-bottom-right-radius': '2em / 30%', 281 | // } 282 | ``` 283 | 284 | ### [getShorthandsForProperty(property: string): Array<string>](./src/getShorthandsForProperty.js) 285 | 286 | This function is the inverse of `getShorthandComputedProperties`. 287 | 288 | It returns all properties that set the given property, including the property itself. 289 | If the property is unknown, an empty array is returned. 290 | 291 | ##### Examples 292 | 293 | ```js 294 | console.log(getShorthandsForProperty('border-left-width')); 295 | // => [ 'border-left-width', 'border-left', 'border-width', 'border' ] 296 | ``` 297 | 298 | ```js 299 | console.log(getShorthandsForProperty('float')); 300 | // => [ 'float' ] 301 | ``` 302 | 303 | ```js 304 | console.log(getShorthandsForProperty('unknown')); 305 | // => [ ] 306 | ``` 307 | 308 | ### [isInitialValue(property, value)](./src/getShorthandsForProperty.js) 309 | 310 | Because of the `initial` keyword and shorthand expansion, 311 | there are many possible values that are equivalently identical 312 | with the initial value of a css property. This function 313 | returns true for all possible values that have the effect of 314 | setting a property to its initial value. 315 | 316 | * `property` - string. the property to which the value is assigned. 317 | * `value` string. the value to check. 318 | 319 | ##### Examples 320 | 321 | ```js 322 | console.log(isInitialValue('padding-left', '0')); 323 | // => true 324 | console.log(isInitialValue('padding-left', '0px')); 325 | // => true 326 | console.log(isInitialValue('padding-left', '1px')); 327 | // => false 328 | console.log(isInitialValue('padding', '0')); 329 | // => true 330 | console.log(isInitialValue('padding', '0 0px 0in')); 331 | // => true 332 | console.log(isInitialValue('padding', '1px')); 333 | // => false 334 | ``` 335 | 336 | ### [initialValues(property, recursivelyResolve, includeShorthands)](./src/initialValueMap.js) 337 | 338 | Get the initial values for a property. 339 | 340 | Returns the initial value or values a property has by 341 | default according the CSS specification. If the property's initial 342 | value(s) is/are unknown, the global keyword `initial` is returned. 343 | 344 | * `property` - (string) the property name 345 | * `recursivelyResolve` - (boolean) when given a shorthand property, 346 | causes the result to include long hand values. 347 | * `includeShorthands` - (boolean) when resolving recursively, causes the 348 | the result to include the specified shorthand property as well as any 349 | intermediate shorthands of this property set to the initial value. 350 | 351 | ##### Examples 352 | 353 | ```js 354 | console.log(initialValues('border-width')); 355 | // => { 'border-width': 'medium' } 356 | console.log(initialValues('border-width', true)); 357 | // => { 358 | // 'border-bottom-width': 'medium', 359 | // 'border-left-width': 'medium', 360 | // 'border-right-width': 'medium', 361 | // 'border-top-width': 'medium', 362 | // 'border-width': 'medium' 363 | // } 364 | ``` 365 | 366 | ### [initialValue(property)](./src/initialValueMap.js) 367 | 368 | Get the initial value for a property. Returns a string that is the initial 369 | value has by default according the CSS specification. If the property's 370 | initial value is unknown, the global keyword `initial` is returned. 371 | 372 | * `property` - the css property name 373 | 374 | ##### Examples 375 | 376 | ```js 377 | console.log(initialValue('border-width')); 378 | => 'medium' 379 | ``` 380 | 381 | ### Developer/Contribution HOWTO 382 | 383 | To use a locally-built version of `css-values-parser`: 384 | 385 | ``` 386 | $ npm install 387 | $ npm run start 388 | $ npm test 389 | ``` 390 | 391 | This will generate grammars and javascript code required to parse the 392 | css properties. 393 | -------------------------------------------------------------------------------- /css-property-parser.d.ts: -------------------------------------------------------------------------------- 1 | export = CssPropertyParser; 2 | 3 | declare namespace CssPropertyParser { 4 | class ParseError extends Error { 5 | } 6 | class UnsupportedPropertyError extends Error { 7 | readonly property: string; 8 | constructor(property: string); 9 | } 10 | class UnknownPropertyError extends Error { 11 | readonly property: string; 12 | constructor(property: string); 13 | } 14 | interface Declarations { 15 | [property: string]: string; 16 | } 17 | 18 | /** 19 | * Given a property and value attempts to expand the value into its longhand 20 | * equivalents. Returns an object mapping the property longhand names to the 21 | * longhand values. If the property cannot be expanded (i.e. the property is 22 | * not a shorthand property) simply returns an object mapping the original 23 | * property to the original value. 24 | * 25 | * @param {string} propertyName - the property name for the given value 26 | * @param {string} propertyValue - the value of the property 27 | * @param {boolean} [recursivelyResolve=true] - recursively resolve additional 28 | * longhand properties if the shorthands expand to additional shorthands. For 29 | * example, the border property expands to border-width, which expands 30 | * further to border-left-width, border-right-width, etc. 31 | * @param {boolean} [includeInitialValues=false] - when expanding the shorthand 32 | * property, fill in any missing longhand values with their initial value. 33 | * For example, the property declaration "border: 1px" only explicitly sets the 34 | * "border-width" longhand property. If this param is true, the returned object 35 | * will fill in the initial values for "border-style" and "border-color". By 36 | * default, the returned object will only contain the "border-width". 37 | */ 38 | function expandShorthandProperty( 39 | propertyName: string, 40 | propertyValue: string, 41 | recursivelyResolve?: boolean, 42 | includeInitialValues?: boolean, 43 | ): Declarations; 44 | 45 | /** 46 | * Given a shorthand property, returns an array of the computed properties for 47 | * that shorthand property. If given a known property that is not a shorthand, 48 | * simply returns the given property. If given an unknown property, returns an 49 | * empty array. 50 | * 51 | * @param {string} shorthandProperty - the shorthand property name. For 52 | * example, "background" or "border". 53 | * @param {boolean} recursivelyResolve - recursively resolve additional 54 | * longhand properties if the shorthands expand to additional shorthands. For 55 | * example, the border property expands to border-width, which expands further 56 | * to border-left-width, border-right-width, etc. Defaults to false. 57 | * @returns {Array} - an array containing the computed properties for the given 58 | * shorthand property. Returns an empty array if the given property is not a 59 | * valid property. 60 | */ 61 | function getShorthandComputedProperties( 62 | shorthandProperty: string, 63 | recursivelyResolve?: boolean 64 | ): Array; 65 | 66 | /** 67 | * Checks if a given property is a shorthand property 68 | * @param {String} property - the property name 69 | * @returns {boolean} - true if property is a shorthand, false otherwise 70 | */ 71 | function isShorthandProperty( 72 | shorthandProperty: string 73 | ): boolean; 74 | 75 | /** 76 | * Return a list of all properties that set the given property. 77 | * Includes at least the value provided, plus any other shorthands that can 78 | * set it. 79 | */ 80 | function getShorthandsForProperty( 81 | longhandProperty: string 82 | ): Array; 83 | /** 84 | * Checks if the given property, value pair is valid. 85 | * 86 | * @param {String} property - the property name. For example, 'border' or 'color'. 87 | * @param {String} value - the property value. For example, '1px solid black'. 88 | * @return {boolean} - true if the given value is valid for the property. Else, false. 89 | */ 90 | function isValidDeclaration( 91 | property: string, 92 | value: string 93 | ): boolean; 94 | 95 | /** 96 | * Because of the `initial` keyword and shorthand expansion, 97 | * there are many possible values that are equivalently identical 98 | * with the initial value of a css property. This function 99 | * returns true for all possible values that have the effect of 100 | * setting a property to its initial value. 101 | * 102 | * @param property the property to which the value is assigned 103 | * @param value the value to check 104 | * @return whether the value is equivalent to the initial value. 105 | */ 106 | function isInitialValue( 107 | property: string, 108 | value: string 109 | ): boolean; 110 | 111 | /** 112 | * Warms up the initial value cache for all known css properties. 113 | * It is not usually necessary to call this function but 114 | * may be useful in some performance testing scenarios. 115 | */ 116 | function computeInitialValues(): void; 117 | 118 | /** 119 | * Get the initial values for a property. 120 | * @param property - the property name 121 | * @param recursivelyResolve - Defaults to false. when given a shorthand property, 122 | * causes the result to include long hand values. 123 | * @param includeShorthands - Defaults to false. when resolving recursively, causes the 124 | * the result to include the specified shorthand property as well as any 125 | * intermediate shorthands of this property to to the initial value. 126 | * @return the initial value or values a property has by 127 | * default according the CSS specification. If the property's initial 128 | * value(s) is/are unknown, the global keyword `initial` is returned. 129 | */ 130 | function initialValues( 131 | property: string, 132 | recursivelyResolve?: boolean, 133 | includeShorthands?: boolean 134 | ): Declarations; 135 | 136 | /** 137 | * Get the initial value for a property. the property can be a shorthand or a 138 | * longhand. 139 | * @param property - the property name 140 | * @return {string} the initial value has by default according the CSS 141 | * specification. If the property's initial value is unknown, the global 142 | * keyword `initial` is returned. There's no spec value for shorthands 143 | * so in those cases, the value returned is not the only legal value that can be returned, 144 | * instead, it is a value that developers is commonly in practice. 145 | */ 146 | function initialValue(property: string): string; 147 | } 148 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-property-parser", 3 | "version": "1.0.6", 4 | "description": "Validate css properties and expand shorthand css properties", 5 | "keywords": [ 6 | "css" 7 | ], 8 | "bugs": "https://github.com/mahirshah/css-property-parser/issues", 9 | "main": "src/index.js", 10 | "scripts": { 11 | "prepublish": "npm start", 12 | "test": "mocha --reporter spec --recursive", 13 | "start": "./updateCSSData.sh", 14 | "clean": "rm -rf src/formatted-data src/grammars/generated", 15 | "benchmark": "node test/benchmark.js", 16 | "doctoc": "doctoc README.md", 17 | "lint": "eslint src/", 18 | "precommit-msg": "echo 'Pre-commit checks...' && exit 0" 19 | }, 20 | "pre-commit": [ 21 | "precommit-msg", 22 | "lint", 23 | "test" 24 | ], 25 | "author": "mahirshah", 26 | "license": "(MIT AND MPL-2.0)", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/mahirshah/css-property-parser" 30 | }, 31 | "types": "css-property-parser.d.ts", 32 | "devDependencies": { 33 | "async": "^2.5.0", 34 | "benchmark": "^2.1.4", 35 | "chai": "^4.1.1", 36 | "css-values": "^0.1.0", 37 | "doctoc": "^1.3.0", 38 | "eslint": "^3.19.0", 39 | "eslint-config-airbnb": "^15.1.0", 40 | "eslint-plugin-import": "^2.7.0", 41 | "microtime": "^2.1.6", 42 | "mocha": "^3.5.0", 43 | "pre-commit": "^1.2.2", 44 | "postcss-value-parser": "^3.3.0", 45 | "sinon": "^2.4.1", 46 | "mdn-data": "1.0.0" 47 | }, 48 | "dependencies": { 49 | "fs-extra": "^3.0.1", 50 | "moo": "^0.4.1", 51 | "nearley": "^2.11.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/constants/css.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globalValues: ['inherit', 'initial', 'unset'], 3 | variableRegex: /var\(--[^)]+\)/, 4 | }; 5 | -------------------------------------------------------------------------------- /src/constants/genericLexer.js: -------------------------------------------------------------------------------- 1 | const moo = require('moo'); 2 | 3 | module.exports = moo.compile({ 4 | color: { 5 | match: /(?:#(?:[0-9a-fA-F]{2}){2,4}|#[0-9a-fA-F]{3}|(?:rgba?|hsla?)\(\s*(?:\d+%?(?:deg|rad|grad|turn)?(?:,|\s)+){2,3}[\s/]*[\d.]+%?\s*\))/, 6 | value: match => match.toLowerCase(), 7 | }, 8 | number: /(?:[+-])?(?:(?:\d+\.\d*)|(?:\d*\.\d+))/, 9 | integer: /(?:[+-])?[0-9]+/, 10 | string: /(?:"[^"]")|(?:'[^']')/, 11 | functionStart: /[a-zA-Z]+\(/, 12 | customIdent: { 13 | match: /[^0-9\s](?:[a-zA-Z0-9_-]|(?:\\.))*/, 14 | value: match => match.toLowerCase(), 15 | }, 16 | ident: { 17 | match: /[a-zA-Z-]+/, 18 | value: match => match.toLowerCase(), 19 | }, 20 | char: { 21 | match: /./, 22 | value: match => match.toLowerCase(), 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /src/constants/grammars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | R_GRAMMAR_IDENT_GLOBAL: /<(([^>( ]+)(\(\))?)>/g, 3 | R_GRAMMAR_IDENT: /<(([^>(]+)(\(\))?)>/, 4 | R_GRAMMAR_IDENT_NAME_GLOBAL: /(([^>(]+)(\(\))?)/g, 5 | BASE_GRAMMAR_RULE_NAME: 'Base', 6 | TOP_LEVEL_NODE_NAME: 'Exp', 7 | GRAMMAR_FILE_EXTENSION: 'ne', 8 | }; 9 | -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CSS: require('./css'), 3 | GRAMMARS: require('./grammars'), 4 | PATHS: require('./paths'), 5 | SYNTAX_OVERRIDES: require('./syntaxOverrides.json'), 6 | }; 7 | -------------------------------------------------------------------------------- /src/constants/paths.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | JSON_GRAMMAR_PATH: path.join(__dirname, '..', 'grammars', 'json'), 5 | NEARLEY_PROPERTY_GRAMMAR_PATH: path.join(__dirname, '..', 'grammars', 'nearley', 'properties'), 6 | GENERATED_JSON_GRAMMAR_PATH: path.join(__dirname, '..', 'grammars', 'generated', 'json'), 7 | GENERATED_NEARLEY_GRAMMAR_PATH: path.join(__dirname, '..', 'grammars', 'generated', 'nearley'), 8 | GENERATED_JS_GRAMMAR_PATH: path.join(__dirname, '..', 'grammars', 'generated', 'js'), 9 | FORMATTED_DATA_PATH: path.join(__dirname, '..', 'formatted-data'), 10 | FORMAL_SYNTAX_GRAMMAR_PATH: path.join(__dirname, '..', 'grammars'), 11 | NEARLEY_BIN_ROOT: path.join(__dirname, '..', '..', 'node_modules', 'nearley', 'bin'), 12 | }; 13 | -------------------------------------------------------------------------------- /src/constants/shorthandIdentToLonghandPropertyMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "transition": { 3 | "SingleTransitionTimingFunction": "transition-timing-function", 4 | "SingleTransitionProperty": "transition-property", 5 | "Time": [ 6 | "transition-duration", 7 | "transition-delay" 8 | ], 9 | "SingleTransition": "" 10 | }, 11 | "mask": { 12 | "MaskClip": "mask-clip", 13 | "MaskOrigin": "mask-origin" 14 | }, 15 | "animation": { 16 | "SingleAnimationPlayState": "animation-play-state", 17 | "SingleAnimationFillMode": "animation-fill-mode", 18 | "SingleAnimationIterationCount": "animation-iteration-count", 19 | "SingleAnimationDirection": "animation-direction", 20 | "SingleTimingFunction": "animation-timing-function", 21 | "KeyframesName": "animation-name", 22 | "Time": [ 23 | "animation-duration", 24 | "animation-delay" 25 | ], 26 | "SingleAnimation": "" 27 | }, 28 | "flex": { 29 | "FlexGrow": "flex-grow", 30 | "FlexShrink": "flex-shrink", 31 | "FlexBasis": "flex-basis" 32 | }, 33 | "background": { 34 | "BgImage": "background-image", 35 | "BackgroundColor": "background-color", 36 | "Box": [ 37 | "background-origin", 38 | "background-clip" 39 | ], 40 | "Position": "background-position", 41 | "BgSize": "background-size", 42 | "RepeatStyle": "background-repeat", 43 | "Attachment": "background-attachment" 44 | }, 45 | "font": { 46 | "FontStyle": "font-style", 47 | "FontVariantCss21": "font-variant", 48 | "FontWeight": "font-weight", 49 | "FontStretch": "font-stretch", 50 | "FontSize": "font-size", 51 | "LineHeight": "line-height", 52 | "FontFamily": "font-family" 53 | }, 54 | "border-radius": [ 55 | "border-top-left-radius", 56 | "border-top-right-radius", 57 | "border-bottom-right-radius", 58 | "border-bottom-left-radius" 59 | ], 60 | "padding": [ 61 | "padding-top", 62 | "padding-right", 63 | "padding-bottom", 64 | "padding-left" 65 | ], 66 | "margin": [ 67 | "margin-top", 68 | "margin-right", 69 | "margin-bottom", 70 | "margin-left" 71 | ], 72 | "border-width": [ 73 | "border-top-width", 74 | "border-right-width", 75 | "border-bottom-width", 76 | "border-left-width" 77 | ], 78 | "border-style": [ 79 | "border-top-style", 80 | "border-right-style", 81 | "border-bottom-style", 82 | "border-left-style" 83 | ], 84 | "border-color": [ 85 | "border-top-color", 86 | "border-right-color", 87 | "border-bottom-color", 88 | "border-left-color" 89 | ], 90 | "text-emphasis": { 91 | "TextEmphasisColor": "text-emphasis-color", 92 | "TextEmphasisStyle": "text-emphasis-style" 93 | }, 94 | "text-decoration": { 95 | "TextDecorationColor": "text-decoration-color", 96 | "TextDecorationStyle": "text-decoration-style", 97 | "TextDecorationLine": "text-decoration-line" 98 | }, 99 | "outline": { 100 | "OutlineStyle": "outline-style", 101 | "OutlineWidth": "outline-width", 102 | "OutlineColor": "outline-color" 103 | }, 104 | "list-style": { 105 | "ListStyleType": "list-style-type", 106 | "ListStylePosition": "list-style-position", 107 | "ListStyleImage": "list-style-image" 108 | }, 109 | "flex-flow": { 110 | "FlexWrap": "flex-wrap", 111 | "FlexDirection": "flex-direction" 112 | }, 113 | "columns": { 114 | "ColumnCount": "column-count", 115 | "ColumnWidth": "column-width" 116 | }, 117 | "column-rule": { 118 | "ColumnRuleWidth": "column-rule-width", 119 | "ColumnRuleStyle": "column-rule-style", 120 | "ColumnRuleColor": "column-rule-color" 121 | }, 122 | "border-top": { 123 | "Color": "border-top-color", 124 | "BrStyle": "border-top-style", 125 | "BrWidth": "border-top-width" 126 | }, 127 | "border-right": { 128 | "Color": "border-right-color", 129 | "BrStyle": "border-right-style", 130 | "BrWidth": "border-right-width" 131 | }, 132 | "border-left": { 133 | "Color": "border-left-color", 134 | "BrStyle": "border-left-style", 135 | "BrWidth": "border-left-width" 136 | }, 137 | "border-bottom": { 138 | "Color": "border-bottom-color", 139 | "BrStyle": "border-bottom-style", 140 | "BrWidth": "border-bottom-width" 141 | }, 142 | "border-inline-start": { 143 | "Color": "color", 144 | "BorderStyle": "border-style", 145 | "BorderWidth": "border-width" 146 | }, 147 | "border-inline-end": { 148 | "Color": "color", 149 | "BorderStyle": "border-style", 150 | "BorderWidth": "border-width" 151 | }, 152 | "border-block-start": { 153 | "Color": "color", 154 | "BorderStyle": "border-style", 155 | "BorderWidth": "border-width" 156 | }, 157 | "border-block-end": { 158 | "Color": "color", 159 | "BorderStyle": "border-style", 160 | "BorderWidth": "border-width" 161 | }, 162 | "border": { 163 | "Color": "border-color", 164 | "BrStyle": "border-style", 165 | "BrWidth": "border-width" 166 | } 167 | } -------------------------------------------------------------------------------- /src/constants/shorthandProperties.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CLASSIFICATIONS: { 3 | TRBL: 'TRBL', 4 | COMMA_SEPARATED_LIST: 'COMMA_SEPARATED_LIST', 5 | UNORDERED_OPTIONAL_TUPLE: 'UNORDERED_OPTIONAL_TUPLE', 6 | AND_LIST: 'AND_LIST', 7 | OTHER: 'OTHER', 8 | FLEX: 'FLEX', 9 | BACKGROUND: 'BACKGROUND', 10 | BORDER_RADIUS: 'BORDER_RADIUS', 11 | FONT: 'FONT', 12 | }, 13 | R_CLASSIFICATION_MAP: { 14 | TRBL: /^\[? ?<['a-z-]+> ?(\| ? ?)*]?\{1,4\}$/, // top left bottom right properties 15 | COMMA_SEPARATED_LIST: /<[a-z-]+>#/, // # 16 | UNORDERED_OPTIONAL_TUPLE: /^\[? ?<['a-z-]+> (\|\| ? ?)*]?$/, // || || .... 17 | AND_LIST: /^\[? ?<['a-z-]+> (&& ? ?)*]?$/, // && && ... 18 | }, 19 | OTHER_PROPERTY_CLASSIFICATION_MAP: { 20 | FLEX: ['flex'], 21 | BACKGROUND: ['background'], 22 | BORDER_RADIUS: ['border-radius'], 23 | FONT: ['font'], 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/constants/syntaxOverrides.json: -------------------------------------------------------------------------------- 1 | { 2 | "list-style": "<'list-style-position'> || <'list-style-image'> || <'list-style-type'>", 3 | "step-timing-function": "step-start | step-end | [ steps( [ , [ start | end ] ]? ) ]", 4 | "cubic-bezier-timing-function": "ease | ease-in | ease-out | ease-in-out | [ cubic-bezier( , , , ) ]", 5 | "single-transition": "