css-tree
Advanced tools
Comparing version 1.0.0-alpha7 to 1.0.0-alpha8
@@ -1,2 +0,3 @@ | ||
var mozilla = require('./mozilla-cssdata.json'); | ||
var mdnProperties = require('./mdn-data-properties.json'); | ||
var mdnSyntaxes = require('./mdn-data-syntaxes.json'); | ||
var patch = require('./patch.json'); | ||
@@ -12,2 +13,3 @@ var data = { | ||
.replace(/>/g, '>') | ||
.replace(/ /g, ' ') | ||
.replace(/&/g, '&'); | ||
@@ -18,6 +20,10 @@ } | ||
for (var key in patch.properties) { | ||
if (key in mozilla.properties) { | ||
mozilla.properties[key].syntax = patch.properties[key].syntax; | ||
if (key in mdnProperties) { | ||
if (patch.properties[key]) { | ||
mdnProperties[key].syntax = patch.properties[key].syntax; | ||
} else { | ||
delete mdnProperties[key]; | ||
} | ||
} else { | ||
mozilla.properties[key] = patch.properties[key]; | ||
mdnProperties[key] = patch.properties[key]; | ||
} | ||
@@ -28,17 +34,17 @@ } | ||
if (patch.syntaxes[key].syntax) { | ||
mozilla.syntaxes[key] = patch.syntaxes[key].syntax; | ||
mdnSyntaxes[key] = patch.syntaxes[key].syntax; | ||
} else { | ||
delete mozilla.syntaxes[key]; | ||
delete mdnSyntaxes[key]; | ||
} | ||
} | ||
// normalize source mozilla syntaxes, since it uses html token | ||
for (var key in mozilla.properties) { | ||
data.properties[key] = normalizeSyntax(mozilla.properties[key].syntax); | ||
// normalize source mdnProperties syntaxes, since it uses html token | ||
for (var key in mdnProperties) { | ||
data.properties[key] = normalizeSyntax(mdnProperties[key].syntax); | ||
} | ||
for (var key in mozilla.syntaxes) { | ||
data.types[key] = normalizeSyntax(mozilla.syntaxes[key]); | ||
for (var key in mdnSyntaxes) { | ||
data.types[key] = normalizeSyntax(mdnSyntaxes[key]); | ||
} | ||
module.exports = data; |
{ | ||
"properties": { | ||
"--*": null, | ||
"-moz-background-clip": { | ||
@@ -122,9 +123,5 @@ "comment": "deprecated syntax in old Firefox, https://developer.mozilla.org/en/docs/Web/CSS/background-clip", | ||
}, | ||
"-webkit-mask-attachment": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<attachment> [, <attachment> ]*" | ||
}, | ||
"-webkit-mask-box-image": { | ||
"comment": "missed; https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-mask-box-image", | ||
"syntax": "[ <uri> | <gradient> | none ] [ <length-percentage>{4} <-webkit-mask-box-repeat>{2} ]?" | ||
"syntax": "[ <url> | <gradient> | none ] [ <length-percentage>{4} <-webkit-mask-box-repeat>{2} ]?" | ||
}, | ||
@@ -135,26 +132,2 @@ "-webkit-mask-clip": { | ||
}, | ||
"-webkit-mask-composite": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<composite-style> [, <composite-style> ]*" | ||
}, | ||
"-webkit-mask-image": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<mask-image> [, <mask-image> ]*" | ||
}, | ||
"-webkit-mask-origin": { | ||
"comment": "missed spaces between brackets, extra space between [ and ,", | ||
"syntax": "[ padding | border | content ] [, [ border | padding | content ] ]*" | ||
}, | ||
"-webkit-mask-position": { | ||
"comment": "# instead of *, extra space between [ and ,", | ||
"syntax": "<mask-position> [, <mask-position> ]*" | ||
}, | ||
"-webkit-mask-position-y": { | ||
"comment": "extra space after `top`", | ||
"syntax": "[ <length-percentage> | top | center | bottom ]#" | ||
}, | ||
"-webkit-mask-repeat": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<repeat-style> [, <repeat-style> ]*" | ||
}, | ||
"-webkit-overflow-scrolling": { | ||
@@ -171,6 +144,2 @@ "comment": "missed; https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling", | ||
}, | ||
"-webkit-tap-highlight-color": { | ||
"comment": "broken syntax", | ||
"syntax": "<color>#" | ||
}, | ||
"-webkit-text-security": { | ||
@@ -195,25 +164,2 @@ "comment": "missed; http://help.dottoro.com/lcbkewgt.php", | ||
}, | ||
"animation-timing-function": { | ||
"comment": "<timing-function> -> <single-timing-function> https://drafts.csswg.org/css-animations/#animation-timing-function", | ||
"syntax": "<single-timing-function>#" | ||
}, | ||
"appearance": { | ||
"comment": "CSS Basic User Interface Module Level 4", | ||
"references": [ | ||
"https://drafts.csswg.org/css-ui-4/#appearance-switching" | ||
], | ||
"syntax": "auto | none" | ||
}, | ||
"azimuth": { | ||
"comment": "missed space", | ||
"syntax": "<angle> | [ [ left-side | far-left | left | center-left | center | center-right | right | far-right | right-side ] || behind ] | leftwards | rightwards" | ||
}, | ||
"background-position-x": { | ||
"comment": "added missed multiplicator `?` https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands", | ||
"syntax": "[ center | [ left | right | x-start | x-end ]? <length-percentage>? ]#" | ||
}, | ||
"background-position-y": { | ||
"comment": "added missed multiplicator `?` https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands", | ||
"syntax": "[ center | [ top | bottom | y-start | y-end ]? <length-percentage>? ]#" | ||
}, | ||
"baseline-shift": { | ||
@@ -247,7 +193,7 @@ "comment": "added SVG property", | ||
"comment": "https://www.w3.org/TR/css3-speech/#property-index", | ||
"syntax": "<uri> <decibel>? | none" | ||
"syntax": "<url> <decibel>? | none" | ||
}, | ||
"cue-before": { | ||
"comment": "https://www.w3.org/TR/css3-speech/#property-index", | ||
"syntax": "<uri> <decibel>? | none" | ||
"syntax": "<url> <decibel>? | none" | ||
}, | ||
@@ -257,3 +203,3 @@ "cursor": { | ||
"refenrences": ["https://www.sitepoint.com/css3-cursor-styles/"], | ||
"syntax": "[ [ <uri> [ <x> <y> ]? , ]* [ auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing | hand | -webkit-grab | -webkit-grabbing | -webkit-zoom-in | -webkit-zoom-out | -moz-grab | -moz-grabbing | -moz-zoom-in | -moz-zoom-out ] ]" | ||
"syntax": "[ [ <url> [ <x> <y> ]? , ]* [ auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing | hand | -webkit-grab | -webkit-grabbing | -webkit-zoom-in | -webkit-zoom-out | -moz-grab | -moz-grabbing | -moz-zoom-in | -moz-zoom-out ] ]" | ||
}, | ||
@@ -305,17 +251,5 @@ "display": { | ||
"font": { | ||
"comment": "wrong quotes", | ||
"comment": "extend with non-standart fonts", | ||
"syntax": "[ [ <'font-style'> || <font-variant-css21> || <'font-weight'> || <'font-stretch'> ]? <'font-size'> [ / <'line-height'> ]? <'font-family'> ] | caption | icon | menu | message-box | small-caption | status-bar | <-non-standart-font>" | ||
}, | ||
"font-variant": { | ||
"comment": "# should stick to term, missed spaces for function body, trailing space", | ||
"syntax": "normal | none | [ <common-lig-values> || <discretionary-lig-values> || <historical-lig-values> || <contextual-alt-values> || stylistic( <feature-value-name> ) || historical-forms || styleset( <feature-value-name># ) || character-variant( <feature-value-name># ) || swash( <feature-value-name> ) || ornaments( <feature-value-name> ) || annotation( <feature-value-name> ) || [ small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps ] || <numeric-figure-values> || <numeric-spacing-values> || <numeric-fraction-values> || ordinal || slashed-zero || <east-asian-variant-values> || <east-asian-width-values> || ruby ]" | ||
}, | ||
"font-variant-alternates": { | ||
"comment": "# should stick to term, missed spaces for function body, trailing space", | ||
"syntax": "normal | [ stylistic( <feature-value-name> ) || historical-forms || styleset( <feature-value-name># ) || character-variant( <feature-value-name># ) || swash( <feature-value-name> ) || ornaments( <feature-value-name> ) || annotation( <feature-value-name> ) ]" | ||
}, | ||
"font-variant-east-asian": { | ||
"comment": "trailing space", | ||
"syntax": "normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]" | ||
}, | ||
"glyph-orientation-horizontal": { | ||
@@ -344,2 +278,5 @@ "comment": "added SVG property", | ||
"comment": "fix syntax <length> -> <length-percentage>", | ||
"references": [ | ||
"https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/letter-spacing" | ||
], | ||
"syntax": "normal | <length-percentage>" | ||
@@ -411,10 +348,2 @@ }, | ||
}, | ||
"scroll-snap-points-x": { | ||
"comment": "missed spaces in function body", | ||
"syntax": "none | repeat( <length> )" | ||
}, | ||
"scroll-snap-points-y": { | ||
"comment": "missed spaces in function body", | ||
"syntax": "none | repeat( <length> )" | ||
}, | ||
"shape-rendering": { | ||
@@ -502,18 +431,2 @@ "comment": "added SVG property", | ||
}, | ||
"text-emphasis-style": { | ||
"comment": "missed between brackets", | ||
"syntax": "none | [ [ filled | open ] || [ dot | circle | double-circle | triangle | sesame ] ] | <string>" | ||
}, | ||
"text-indent": { | ||
"comment": "wrong syntax, replaced for https://drafts.csswg.org/css-text-3/#text-indent-property", | ||
"syntax": "[ <length-percentage> ] && hanging? && each-line?" | ||
}, | ||
"text-size-adjust": { | ||
"comment": "trailing space", | ||
"syntax": "none | auto | <percentage>" | ||
}, | ||
"touch-action": { | ||
"comment": "missed between brackets", | ||
"syntax": "auto | none | [ [ pan-x | pan-left | pan-right ] || [ pan-y | pan-up | pan-down ] ] | manipulation" | ||
}, | ||
"transform-origin": { | ||
@@ -523,6 +436,2 @@ "comment": "move first group to the end since less collecting", | ||
}, | ||
"transition-timing-function": { | ||
"comment": "extra space in the beginning", | ||
"syntax": "<single-transition-timing-function>#" | ||
}, | ||
"unicode-bidi": { | ||
@@ -684,6 +593,2 @@ "comment": "added prefixed keywords https://developer.mozilla.org/en-US/docs/Web/CSS/unicode-bidi", | ||
}, | ||
"an-plus-b": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"attr()": { | ||
@@ -693,6 +598,2 @@ "comment": "drop it since it's a generic", | ||
}, | ||
"blend-mode": { | ||
"comment": "missed, https://drafts.fxtf.org/compositing-1/#ltblendmodegt", | ||
"syntax": "normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity" | ||
}, | ||
"border-radius": { | ||
@@ -714,10 +615,2 @@ "comment": "missed, https://drafts.csswg.org/css-backgrounds-3/#the-border-radius", | ||
}, | ||
"family-name": { | ||
"comment": "upper case", | ||
"syntax": "<string> | <ident>+" | ||
}, | ||
"feature-value-name": { | ||
"comment": "missed angle brackets", | ||
"syntax": "<ident>" | ||
}, | ||
"inset()": { | ||
@@ -754,6 +647,2 @@ "comment": "changed <border-radius> to <'border-radius'>", | ||
}, | ||
"mask-position": { | ||
"comment": "extra space after `top`", | ||
"syntax": "[ <length-percentage> | left | center | right ] [ <length-percentage> | top | center | bottom ]?" | ||
}, | ||
"matrix()": { | ||
@@ -775,6 +664,2 @@ "comment": "redundant max", | ||
}, | ||
"namespace-prefix": { | ||
"comment": "missed angle brackets", | ||
"syntax": "<ident>" | ||
}, | ||
"outline-radius": { | ||
@@ -800,6 +685,2 @@ "comment": "missed, looks like it's a similar to <border-radius> https://developer.mozilla.org/en/docs/Web/CSS/-moz-outline-radius", | ||
}, | ||
"scale3d()": { | ||
"comment": "missed space before comma", | ||
"syntax": "scale3d( <number> , <number> , <number> )" | ||
}, | ||
"shape": { | ||
@@ -809,6 +690,2 @@ "comment": "missed spaces in function body and add backwards compatible syntax", | ||
}, | ||
"single-animation-name": { | ||
"comment": "missed angle brackets", | ||
"syntax": "none | <ident>" | ||
}, | ||
"single-transition": { | ||
@@ -818,10 +695,2 @@ "comment": "moved <single-transition-timing-function> in the beginning to avoid wrong match to <single-transition-property>", | ||
}, | ||
"single-transition-property": { | ||
"comment": "missed angle brackets", | ||
"syntax": "all | <ident>" | ||
}, | ||
"single-transition-timing-function": { | ||
"comment": "missed spaces", | ||
"syntax": "ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps( <integer> [, [ start | end ] ]? ) | cubic-bezier( <number>, <number>, <number>, <number> )" | ||
}, | ||
"svg-length": { | ||
@@ -846,6 +715,2 @@ "comment": "All coordinates and lengths in SVG can be specified with or without a unit identifier", | ||
}, | ||
"uri": { | ||
"comment": "deprecated, add alias to not change syntaxes", | ||
"syntax": "<url>" | ||
}, | ||
"quote": { | ||
@@ -862,4 +727,121 @@ "comment": "missed -> https://drafts.csswg.org/css-content/#typedef-quote", | ||
"syntax": "<number>" | ||
}, | ||
"var()": { | ||
"comment": "drop it since it's a generic (also syntax is incorrect and can't be parsed)", | ||
"syntax": null | ||
}, | ||
"an-plus-b": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-type": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-block": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-declaration": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-block-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-declaration-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"general-enclosed": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"keyframe-block": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"keyframe-block-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"mf-plain": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"mf-range": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"mf-value": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"minmax()": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-and": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-condition": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-not": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-or": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-in-parens": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-feature": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-condition-without-or": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-query": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-query-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-selector": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-selector-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-body": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-margin-box": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-margin-box-type": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"pseudo-page": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
} | ||
} | ||
} |
@@ -0,1 +1,16 @@ | ||
## 1.0.0-alpha8 (November 11, 2016) | ||
- Fixed `Scanner#skip()` issue method when cursor is moving to the end of source | ||
- Simplified `Progid` node | ||
- Changed behaviour for bad selector processing, now parsing fails instead of selector ignoring | ||
- Fixed `<id-selector>` generic syntax | ||
- Added `q` unit for `<length>` generic syntax | ||
- Refactored syntax parser (performance) | ||
- Reduced startup time by implementing lazy syntax parsing (default syntax doesn't parse on module load) | ||
- Updated syntax dictionaries and used [`mdn/data`](https://github.com/mdn/data) instead of `Template:CSSData` | ||
- Renamed `syntax.stringify()` method to `syntax.translate()` | ||
- Simplified generic syntax functions, those functions receive a single AST node for checking and should return `true` or `false` | ||
- Added exception for values that contains `var()`, those values are always valid for now | ||
- Added more tests and increase code coverage to `98.5%` | ||
## 1.0.0-alpha7 (October 7, 2016) | ||
@@ -2,0 +17,0 @@ |
@@ -78,5 +78,8 @@ 'use strict'; | ||
function getInfo() { | ||
function getInfo(start) { | ||
if (needPositions) { | ||
return scanner.getLocation(scanner.tokenStart, filename); | ||
return scanner.getLocation( | ||
start !== undefined ? start : scanner.tokenStart, | ||
filename | ||
); | ||
} | ||
@@ -88,9 +91,5 @@ | ||
function getStylesheet(nested) { | ||
var start = scanner.tokenStart; | ||
var rules = new List(); | ||
var child; | ||
var node = { | ||
type: 'StyleSheet', | ||
info: getInfo(), | ||
rules: rules | ||
}; | ||
@@ -104,2 +103,9 @@ scan: | ||
case RIGHTCURLYBRACKET: | ||
if (!nested) { | ||
scanner.error('Unexpected right curly brace'); | ||
} | ||
break scan; | ||
case COMMENT: | ||
@@ -119,9 +125,2 @@ // ignore comments except exclamation comments (i.e. /*! .. */) on top level | ||
case RIGHTCURLYBRACKET: | ||
if (!nested) { | ||
scanner.error('Unexpected right curly brace'); | ||
} | ||
break scan; | ||
default: | ||
@@ -134,3 +133,7 @@ child = getRule(); | ||
return node; | ||
return { | ||
type: 'StyleSheet', | ||
info: getInfo(start), | ||
rules: rules | ||
}; | ||
} | ||
@@ -154,10 +157,6 @@ | ||
function getAtruleExpression() { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var wasSpace = false; | ||
var child; | ||
var node = { | ||
type: 'AtruleExpression', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
@@ -210,3 +209,7 @@ readSC(); | ||
return node; | ||
return { | ||
type: 'AtruleExpression', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
@@ -225,3 +228,2 @@ | ||
// at-rule expression can ends with semicolon, left curly bracket or eof | ||
switch (scanner.tokenType) { | ||
@@ -241,2 +243,4 @@ case SEMICOLON: | ||
break; | ||
// at-rule expression can ends with semicolon, left curly bracket or eof - no other options | ||
} | ||
@@ -248,20 +252,21 @@ | ||
function getRule() { | ||
return { | ||
var node = { | ||
type: 'Rule', | ||
info: getInfo(), | ||
selector: getSelector(), | ||
block: getBlockWithBrackets() | ||
block: null | ||
}; | ||
scanner.eat(LEFTCURLYBRACKET); | ||
node.block = getBlock(); | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
return node; | ||
} | ||
function getSelector() { | ||
var start = scanner.tokenStart; | ||
var selectors = new List(); | ||
var simpleSelector; | ||
var isBadSelector = false; | ||
var lastComma = true; | ||
var node = { | ||
type: 'Selector', | ||
info: getInfo(), | ||
selectors: selectors | ||
}; | ||
var lastComma = -2; | ||
@@ -275,7 +280,7 @@ scan: | ||
case COMMA: | ||
if (lastComma) { | ||
isBadSelector = true; | ||
if (lastComma !== -1) { | ||
scanner.error('Unexpected comma'); | ||
} | ||
lastComma = true; | ||
lastComma = scanner.tokenStart; | ||
scanner.next(); | ||
@@ -285,7 +290,3 @@ break; | ||
default: | ||
if (!lastComma) { | ||
isBadSelector = true; | ||
} | ||
lastComma = false; | ||
lastComma = -1; | ||
simpleSelector = getSimpleSelector(); | ||
@@ -295,3 +296,3 @@ selectors.appendData(simpleSelector); | ||
if (simpleSelector.sequence.isEmpty()) { | ||
isBadSelector = true; | ||
scanner.error('Simple selector expected'); | ||
} | ||
@@ -301,15 +302,15 @@ } | ||
if (lastComma) { | ||
isBadSelector = true; | ||
// scanner.error('Unexpected trailing comma'); | ||
if (lastComma !== -1 && lastComma !== -2) { // TODO: fail on empty selector rules? | ||
scanner.error('Unexpected trailing comma', lastComma); | ||
} | ||
if (isBadSelector) { | ||
selectors.clear(); | ||
} | ||
return node; | ||
return { | ||
type: 'Selector', | ||
info: getInfo(start), | ||
selectors: selectors | ||
}; | ||
} | ||
function getSimpleSelector(nested) { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
@@ -319,7 +320,2 @@ var combinator = null; | ||
var child; | ||
var node = { | ||
type: 'SimpleSelector', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
@@ -381,3 +377,3 @@ scan: | ||
case NUMBERSIGN: | ||
child = getShash(); | ||
child = getId(); | ||
break; | ||
@@ -397,3 +393,3 @@ | ||
case NUMBER: | ||
child = getPercentage(getInfo(), readNumber()); | ||
child = getPercentage(); | ||
break; | ||
@@ -410,3 +406,3 @@ | ||
type: 'Combinator', | ||
info: needPositions ? scanner.getLocation(combinatorOffset, filename) : null, | ||
info: getInfo(combinatorOffset), | ||
name: ' ' | ||
@@ -427,6 +423,11 @@ }; | ||
return node; | ||
return { | ||
type: 'SimpleSelector', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
function getDeclarations() { | ||
function getBlock() { | ||
var start = scanner.tokenStart; | ||
var declarations = new List(); | ||
@@ -451,16 +452,5 @@ | ||
return declarations; | ||
} | ||
function getBlockWithBrackets() { | ||
var info = getInfo(); | ||
var declarations; | ||
scanner.eat(LEFTCURLYBRACKET); | ||
declarations = getDeclarations(); | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
return { | ||
type: 'Block', | ||
info: info, | ||
info: getInfo(start), | ||
declarations: declarations | ||
@@ -470,12 +460,4 @@ }; | ||
function getBlock() { | ||
return { | ||
type: 'Block', | ||
info: getInfo(), | ||
declarations: getDeclarations() | ||
}; | ||
} | ||
function getDeclaration(nested) { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var property = readProperty(); | ||
@@ -485,2 +467,3 @@ var important = false; | ||
readSC(); | ||
scanner.eat(COLON); | ||
@@ -495,3 +478,3 @@ value = getValue(nested, property); | ||
type: 'Declaration', | ||
info: info, | ||
info: getInfo(start), | ||
important: important, | ||
@@ -505,7 +488,5 @@ property: property, | ||
var start = scanner.tokenStart; | ||
var name; | ||
var type; | ||
for (; !scanner.eof; scanner.next()) { | ||
var type = scanner.tokenType; | ||
for (; type = scanner.tokenType; scanner.next()) { | ||
if (type !== SOLIDUS && | ||
@@ -519,7 +500,4 @@ type !== ASTERISK && | ||
scanIdent(true); | ||
name = scanner.substrToCursor(start); | ||
readSC(); | ||
return name; | ||
return scanner.substrToCursor(start); | ||
} | ||
@@ -533,10 +511,6 @@ | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var wasSpace = false; | ||
var child; | ||
var node = { | ||
type: 'Value', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
@@ -611,6 +585,29 @@ readSC(); | ||
return node; | ||
return { | ||
type: 'Value', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
// any = string | percentage | dimension | number | uri | functionExpression | funktion | unary | operator | ident | ||
function getFilterValue() { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var progid; | ||
while (progid = checkProgid()) { | ||
readSC(); | ||
sequence.appendData(getProgid(progid)); | ||
} | ||
readSC(); | ||
return { | ||
type: 'Value', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
// any = percentage | dimension | number | operator | ident | function | ||
function getAny(scope) { | ||
@@ -632,20 +629,17 @@ switch (scanner.tokenType) { | ||
case NUMBER: | ||
var info = getInfo(); | ||
var number = readNumber(); | ||
var type = scanner.tokenType; | ||
switch (scanner.lookupType(1)) { | ||
case PERCENTSIGN: | ||
return getPercentage(); | ||
if (type === PERCENTSIGN) { | ||
return getPercentage(info, number); | ||
} | ||
case IDENTIFIER: | ||
return getDimension(); | ||
if (type === IDENTIFIER) { | ||
return getDimension(info, number); | ||
default: | ||
return { | ||
type: 'Number', | ||
info: getInfo(), | ||
value: readNumber() | ||
}; | ||
} | ||
return { | ||
type: 'Number', | ||
info: info, | ||
value: number | ||
}; | ||
default: | ||
@@ -655,3 +649,2 @@ scanner.error(); | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -662,3 +655,3 @@ | ||
if (scanner.tokenType === LEFTPARENTHESIS) { | ||
return getFunction(scope, info, scanner.substrToCursor(start)); | ||
return getFunction(scope, getInfo(start), scanner.substrToCursor(start)); | ||
} | ||
@@ -668,3 +661,3 @@ | ||
type: 'Identifier', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -711,7 +704,5 @@ }; | ||
scanner.eat(LEFTSQUAREBRACKET); | ||
readSC(); | ||
node.name = getAttributeName(); | ||
readSC(); | ||
@@ -748,13 +739,9 @@ | ||
function getParentheses(scope) { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var wasSpace = false; | ||
var child; | ||
var node = { | ||
type: 'Parentheses', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
// left brace | ||
scanner.next(); | ||
scanner.eat(LEFTPARENTHESIS); | ||
readSC(); | ||
@@ -811,3 +798,7 @@ | ||
return node; | ||
return { | ||
type: 'Parentheses', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
@@ -817,3 +808,3 @@ | ||
function getClass() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -824,3 +815,3 @@ scanner.eat(FULLSTOP); | ||
type: 'Class', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false) | ||
@@ -831,4 +822,4 @@ }; | ||
// '#' ident | ||
function getShash() { | ||
var info = getInfo(); | ||
function getId() { | ||
var start = scanner.tokenStart; | ||
@@ -839,3 +830,3 @@ scanner.eat(NUMBERSIGN); | ||
type: 'Id', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false) | ||
@@ -847,3 +838,3 @@ }; | ||
function getCombinator() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var combinator; | ||
@@ -880,3 +871,3 @@ | ||
type: 'Combinator', | ||
info: info, | ||
info: getInfo(start), | ||
name: combinator | ||
@@ -888,3 +879,2 @@ }; | ||
function getComment() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart + 2; | ||
@@ -903,3 +893,3 @@ var end = scanner.tokenEnd; | ||
type: 'Comment', | ||
info: info, | ||
info: getInfo(start), | ||
value: scanner.source.substring(start, end) | ||
@@ -930,7 +920,7 @@ }; | ||
// number ident | ||
function getDimension(info, number) { | ||
function getDimension() { | ||
return { | ||
type: 'Dimension', | ||
info: info, | ||
value: number, | ||
info: getInfo(), | ||
value: readNumber(), | ||
unit: readUnit() | ||
@@ -940,3 +930,6 @@ }; | ||
function getPercentage(info, number) { | ||
function getPercentage() { | ||
var start = scanner.tokenStart; | ||
var number = readNumber(); | ||
scanner.eat(PERCENTSIGN); | ||
@@ -946,3 +939,3 @@ | ||
type: 'Percentage', | ||
info: info, | ||
info: getInfo(start), | ||
value: number | ||
@@ -1063,2 +1056,3 @@ }; | ||
// TODO: -> getSimpleSelectorList | ||
function getNotFunctionArguments() { | ||
@@ -1132,7 +1126,3 @@ var args = new List(); | ||
function getUri(scope, info) { | ||
var node = { | ||
type: 'Url', | ||
info: info, | ||
value: null | ||
}; | ||
var value; | ||
@@ -1143,7 +1133,7 @@ scanner.eat(LEFTPARENTHESIS); // ( | ||
if (scanner.tokenType === STRING) { | ||
node.value = getString(); | ||
value = getString(); | ||
} else { | ||
var rawInfo = getInfo(); | ||
var start = scanner.tokenStart; | ||
// TODO: fix me, looks like incorrect raw scan | ||
for (; !scanner.eof; scanner.next()) { | ||
@@ -1159,5 +1149,5 @@ var type = scanner.tokenType; | ||
node.value = { | ||
value = { | ||
type: 'Raw', | ||
info: rawInfo, | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
@@ -1170,3 +1160,7 @@ }; | ||
return node; | ||
return { | ||
type: 'Url', | ||
info: info, | ||
value: value | ||
}; | ||
} | ||
@@ -1271,4 +1265,2 @@ | ||
} | ||
return hexLength; | ||
} | ||
@@ -1278,3 +1270,2 @@ | ||
var start = scanner.tokenStart; | ||
var info = getInfo(); | ||
@@ -1286,3 +1277,3 @@ scanner.next(); // U or u | ||
type: 'UnicodeRange', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -1319,3 +1310,2 @@ }; | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1356,3 +1346,3 @@ var expectIdentifier = false; | ||
type: 'Identifier', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -1363,3 +1353,2 @@ }; | ||
function getTypeOrUniversal() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1391,3 +1380,3 @@ var universal = false; | ||
type: universal ? 'Universal' : 'Type', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -1419,15 +1408,8 @@ }; | ||
function getNthSelector() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var node; | ||
var name; | ||
scanner.eat(COLON); | ||
node = { | ||
type: 'PseudoClass', | ||
info: info, | ||
name: readIdent(false), | ||
sequence: sequence | ||
}; | ||
name = readIdent(false); | ||
scanner.eat(LEFTPARENTHESIS); | ||
@@ -1443,3 +1425,3 @@ readSC(); | ||
type: 'Nth', | ||
info: getInfo(), | ||
info: info, | ||
value: scanner.substrToCursor(start) | ||
@@ -1583,3 +1565,8 @@ }); | ||
return node; | ||
return { | ||
type: 'PseudoClass', | ||
info: getInfo(start), | ||
name: name, | ||
sequence: sequence | ||
}; | ||
} | ||
@@ -1597,29 +1584,11 @@ | ||
function getOperator() { | ||
var node = { | ||
type: 'Operator', | ||
info: getInfo(), | ||
value: scanner.getTokenValue() | ||
}; | ||
var start = scanner.tokenStart; | ||
scanner.next(); | ||
return node; | ||
} | ||
function getFilterValue() { // TODO | ||
var sequence = new List(); | ||
var progid; | ||
var node = { | ||
type: 'Value', | ||
info: getInfo(), | ||
sequence: sequence | ||
return { | ||
type: 'Operator', | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
}; | ||
while (progid = checkProgid()) { | ||
sequence.appendData(getProgid(progid)); | ||
} | ||
readSC(); | ||
return node; | ||
} | ||
@@ -1629,13 +1598,3 @@ | ||
function checkProgid() { | ||
function checkSC(offset) { | ||
for (var type; type = scanner.lookupType(offset); offset++) { | ||
if (type !== WHITESPACE && type !== COMMENT) { | ||
break; | ||
} | ||
} | ||
return offset; | ||
} | ||
var startOffset = checkSC(0); | ||
var startOffset = findNonSCOffset(0); | ||
var offset = startOffset; | ||
@@ -1653,3 +1612,3 @@ | ||
offset += 2; | ||
offset = checkSC(offset); | ||
offset = findNonSCOffset(offset); | ||
@@ -1665,3 +1624,3 @@ if (scanner.lookupValue(offset + 0, 'dximagetransform') === false || | ||
offset += 5; | ||
offset = checkSC(offset); | ||
offset = findNonSCOffset(offset); | ||
} | ||
@@ -1675,3 +1634,3 @@ | ||
if (type === RIGHTPARENTHESIS) { | ||
return offset - startOffset; | ||
return offset - startOffset + 1; | ||
} | ||
@@ -1684,25 +1643,11 @@ } | ||
function getProgid(progidEnd) { | ||
var node = { | ||
type: 'Progid', | ||
info: getInfo(), | ||
value: null | ||
}; | ||
readSC(); | ||
var rawInfo = getInfo(); | ||
var start = scanner.tokenStart; | ||
scanner.skip(progidEnd); | ||
scanner.eat(RIGHTPARENTHESIS); | ||
node.value = { | ||
type: 'Raw', | ||
info: rawInfo, | ||
return { | ||
type: 'Progid', | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
}; | ||
readSC(); | ||
return node; | ||
} | ||
@@ -1738,3 +1683,3 @@ | ||
function getPseudoElement() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1746,3 +1691,3 @@ scanner.eat(COLON); | ||
type: 'PseudoElement', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false), | ||
@@ -1759,3 +1704,3 @@ legacy: false | ||
function getLegacyPseudoElement() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1766,3 +1711,3 @@ scanner.eat(COLON); | ||
type: 'PseudoElement', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false), | ||
@@ -1775,4 +1720,3 @@ legacy: true | ||
function getPseudoClass() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart + 1; | ||
var start = scanner.tokenStart; | ||
@@ -1783,3 +1727,3 @@ scanner.eat(COLON); | ||
if (scanner.tokenType === LEFTPARENTHESIS) { | ||
return getFunction(SCOPE_SELECTOR, info, scanner.substrToCursor(start)); | ||
return getFunction(SCOPE_SELECTOR, getInfo(start), scanner.substrToCursor(start + 1)); | ||
} | ||
@@ -1789,4 +1733,4 @@ | ||
type: 'PseudoClass', | ||
info: info, | ||
name: scanner.substrToCursor(start), | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start + 1), | ||
sequence: null | ||
@@ -1796,2 +1740,12 @@ }; | ||
function findNonSCOffset(offset) { | ||
for (var type; type = scanner.lookupType(offset); offset++) { | ||
if (type !== WHITESPACE && type !== COMMENT) { | ||
break; | ||
} | ||
} | ||
return offset; | ||
} | ||
function readSC() { | ||
@@ -1801,4 +1755,2 @@ while (scanner.tokenType === WHITESPACE || scanner.tokenType === COMMENT) { | ||
} | ||
return null; | ||
} | ||
@@ -1808,11 +1760,11 @@ | ||
function getString() { | ||
var node = { | ||
type: 'String', | ||
info: getInfo(), | ||
value: scanner.getTokenValue() | ||
}; | ||
var start = scanner.tokenStart; | ||
scanner.next(); | ||
return node; | ||
return { | ||
type: 'String', | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
}; | ||
} | ||
@@ -1822,4 +1774,3 @@ | ||
function getHash() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart + 1; | ||
var start = scanner.tokenStart; | ||
@@ -1842,4 +1793,4 @@ scanner.eat(NUMBERSIGN); | ||
type: 'Hash', | ||
info: info, | ||
value: scanner.substrToCursor(start) | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start + 1) // skip # | ||
}; | ||
@@ -1879,4 +1830,4 @@ } | ||
// fix soft deoptimizations (insufficient type feedback) | ||
parse('a.b#c:e:NOT(a)::g,* b>c+d~e/deep/f,100%{v:1 2em t a(2%, var(--a)) url(..) -foo-bar !important}'); | ||
parse('a.b#c:e:NOT(a)::g,* b >c+d~e/deep/f,100%{v:1 2em t a(2%, var(--a)) url(..) -foo-bar !important}'); | ||
module.exports = parse; |
@@ -33,8 +33,9 @@ 'use strict'; | ||
var MIN_ARRAY_SIZE = 16 * 1024; | ||
var MIN_BUFFER_SIZE = 16 * 1024; | ||
var OFFSET_MASK = 0x00FFFFFF; | ||
var TYPE_OFFSET = 24; | ||
var SafeUint32Array = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; // fallback on Array when TypedArray is not supported | ||
var lastIndexOf = Array.prototype.lastIndexOf; // some browser implementations have no TypedArray#lastIndexOf | ||
var LongArray = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; | ||
var offsetAndType = new LongArray(MIN_ARRAY_SIZE); | ||
var offsetAndType = new SafeUint32Array(MIN_BUFFER_SIZE); | ||
var lines = null; | ||
@@ -56,3 +57,3 @@ | ||
if (lines === null || lines.length < sourceLength + 1) { | ||
lines = new LongArray(Math.max(sourceLength + 1024, MIN_ARRAY_SIZE)); | ||
lines = new SafeUint32Array(Math.max(sourceLength + 1024, MIN_BUFFER_SIZE)); | ||
} | ||
@@ -231,3 +232,3 @@ | ||
if (offsetAndType.length < sourceLength + 1) { | ||
offsetAndType = new LongArray(sourceLength + 1024); | ||
offsetAndType = new SafeUint32Array(sourceLength + 1024); | ||
} | ||
@@ -283,3 +284,3 @@ | ||
offsetAndType[tokenCount++] = (type << 24) | offset; | ||
offsetAndType[tokenCount++] = (type << TYPE_OFFSET) | offset; | ||
prevType = type; | ||
@@ -323,6 +324,6 @@ } | ||
if (offset < this.tokenCount) { | ||
return this.offsetAndType[offset] >> 24; | ||
return this.offsetAndType[offset] >> TYPE_OFFSET; | ||
} | ||
return 0; | ||
return NULL; | ||
}, | ||
@@ -358,5 +359,6 @@ lookupValue: function(offset, referenceStr) { | ||
next = this.offsetAndType[next]; | ||
this.tokenType = next >> 24; | ||
this.tokenType = next >> TYPE_OFFSET; | ||
this.tokenEnd = next & OFFSET_MASK; | ||
} else { | ||
this.currentToken = this.tokenCount; | ||
this.next(); | ||
@@ -372,3 +374,3 @@ } | ||
next = this.offsetAndType[next]; | ||
this.tokenType = next >> 24; | ||
this.tokenType = next >> TYPE_OFFSET; | ||
this.tokenEnd = next & OFFSET_MASK; | ||
@@ -384,16 +386,14 @@ } else { | ||
eat: function(tokenType) { | ||
if (this.tokenType === tokenType) { | ||
this.next(); | ||
return true; | ||
if (this.tokenType !== tokenType) { | ||
this.error(TokenName[tokenType] + ' is expected'); | ||
} | ||
this.error(TokenName[tokenType] + ' is expected'); | ||
this.next(); | ||
}, | ||
expectIdentifier: function(name) { | ||
if (this.tokenType === IDENTIFIER && cmpStr(this.source, this.tokenStart, this.tokenEnd, name)) { | ||
this.next(); | ||
return true; | ||
if (this.tokenType !== IDENTIFIER || cmpStr(this.source, this.tokenStart, this.tokenEnd, name) === false) { | ||
this.error('Identifier `' + name + '` is expected'); | ||
} | ||
this.error('Identifier `' + name + '` is expected'); | ||
this.next(); | ||
}, | ||
@@ -452,3 +452,3 @@ | ||
return Array.prototype.slice.call(this.offsetAndType, 0, this.tokenCount).map(function(item) { | ||
return TokenName[item >> 24]; | ||
return TokenName[item >> TYPE_OFFSET]; | ||
}); | ||
@@ -455,0 +455,0 @@ } |
@@ -1,2 +0,2 @@ | ||
var stringifySyntax = require('./stringify'); | ||
var translateSyntax = require('./translate'); | ||
var translateCss = require('../utils/translate'); | ||
@@ -49,3 +49,3 @@ var walk = require('../utils/walk').all; | ||
error.rawMessage = message; | ||
error.syntax = syntax ? stringifySyntax(syntax) : '<generic>'; | ||
error.syntax = syntax ? translateSyntax(syntax) : '<generic>'; | ||
error.css = css; | ||
@@ -52,0 +52,0 @@ error.line = badNode && badNode.info ? badNode.info.line : undefined; |
var names = require('../utils/names.js'); | ||
// https://www.w3.org/TR/css-values-3/#lengths | ||
var LENGTH = { | ||
@@ -10,2 +12,3 @@ // absolute length units | ||
'pc': true, | ||
'q': true, | ||
@@ -43,2 +46,3 @@ // relative length units | ||
// https://www.w3.org/TR/css-values-3/#resolution | ||
// https://www.w3.org/TR/css3-images/#resolution-type | ||
@@ -80,13 +84,17 @@ var RESOLUTION = { | ||
// there were some prefixed implementations | ||
return keyword.vendor === '' || keyword.vendor === '-moz-' || keyword.vendor === '-webkit-'; | ||
return keyword.vendor === '' || | ||
keyword.vendor === '-moz-' || | ||
keyword.vendor === '-webkit-'; | ||
} | ||
function astNode(type) { | ||
return function(node) { | ||
return node.data.type === type; | ||
}; | ||
} | ||
function dimension(type) { | ||
return function(node) { | ||
if (node && (isCalc(node) || (node.data.type === 'Dimension' && type.hasOwnProperty(node.data.unit.toLowerCase())))) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Dimension' && type.hasOwnProperty(node.data.unit.toLowerCase())); | ||
}; | ||
@@ -96,137 +104,63 @@ } | ||
function length(node) { | ||
if (node && ( | ||
isCalc(node) | ||
|| | ||
(node.data.type === 'Dimension' && LENGTH.hasOwnProperty(node.data.unit.toLowerCase())) | ||
|| | ||
(node.data.type === 'Number' && Number(node.data.value) === 0) | ||
) | ||
) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Dimension' && LENGTH.hasOwnProperty(node.data.unit.toLowerCase())) || | ||
(node.data.type === 'Number' && Number(node.data.value) === 0); | ||
} | ||
function attr(node) { | ||
if (node && node.data.type === 'Function' && node.data.name.toLowerCase() === 'attr') { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return node.data.type === 'Function' && node.data.name.toLowerCase() === 'attr'; | ||
} | ||
function astNode(type) { | ||
return function(node) { | ||
if (node && node.data.type === type) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
}; | ||
} | ||
function number(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Number')) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || node.data.type === 'Number'; | ||
} | ||
function numberZeroOne(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Number')) { | ||
if (isCalc(node) || node.data.type === 'Number') { | ||
var value = Number(node.data.value); | ||
if (value >= 0 && value <= 1) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return value >= 0 && value <= 1; | ||
} | ||
return false; | ||
} | ||
function numberOneOrGreater(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Number')) { | ||
var value = Number(node.data.value); | ||
if (value >= 1) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
if (isCalc(node) || node.data.type === 'Number') { | ||
return Number(node.data.value) >= 1; | ||
} | ||
return false; | ||
} | ||
// TODO: fail on 10e-2 | ||
function integer(node) { | ||
if (node && (isCalc(node) || (node.data.type === 'Number' && node.data.value.indexOf('.') === -1))) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Number' && node.data.value.indexOf('.') === -1); | ||
} | ||
// TODO: fail on 10e-2 | ||
function positiveInteger(node) { | ||
if (node && (isCalc(node) || (node.data.type === 'Number' && node.data.value.indexOf('.') === -1 && node.data.value.charAt(0) !== '-'))) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Number' && node.data.value.indexOf('.') === -1 && node.data.value.charAt(0) !== '-'); | ||
} | ||
function percentage(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Percentage')) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
node.data.type === 'Percentage'; | ||
} | ||
function hexColor(node) { | ||
if (!node || node.data.type !== 'Hash') { | ||
return; | ||
if (node.data.type !== 'Hash') { | ||
return false; | ||
} | ||
var hex = node.data.value; | ||
if (/^[0-9a-fA-F]{3,8}$/.test(hex) && | ||
(hex.length === 3 || hex.length === 4 || hex.length === 6 || hex.length === 8)) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
} | ||
function idSelector(node) { | ||
if (!node || node.data.type !== 'Hash') { | ||
return; | ||
} | ||
var cursor = node.next; | ||
var match = [node.data]; | ||
while (cursor && (cursor.data.type === 'Number' || cursor.data.type === 'Identifier')) { | ||
match.push(cursor.data); | ||
cursor = cursor.next; | ||
} | ||
return { | ||
next: cursor, | ||
match: match | ||
}; | ||
return /^[0-9a-fA-F]{3,8}$/.test(hex) && | ||
(hex.length === 3 || hex.length === 4 || hex.length === 6 || hex.length === 8); | ||
} | ||
function expression(node) { | ||
if (node && node.data.type === 'Function' && node.data.name.toLowerCase() === 'expression') { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return node.data.type === 'Function' && node.data.name.toLowerCase() === 'expression'; | ||
} | ||
@@ -236,4 +170,4 @@ | ||
function customIdent(node) { | ||
if (!node || node.data.type !== 'Identifier') { | ||
return; | ||
if (node.data.type !== 'Identifier') { | ||
return false; | ||
} | ||
@@ -245,3 +179,3 @@ | ||
if (name === 'unset' || name === 'initial' || name === 'inherit') { | ||
return; | ||
return false; | ||
} | ||
@@ -251,6 +185,3 @@ | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
return true; | ||
} | ||
@@ -267,3 +198,3 @@ | ||
'hex-color': hexColor, | ||
'id-selector': idSelector, // element( <id-selector> ) | ||
'id-selector': astNode('Hash'), // element( <id-selector> ) | ||
'ident': astNode('Identifier'), | ||
@@ -270,0 +201,0 @@ 'integer': integer, |
@@ -6,4 +6,4 @@ module.exports = { | ||
parse: require('./parse'), | ||
stringify: require('./stringify'), | ||
translate: require('./translate'), | ||
walk: require('./walk') | ||
}; |
var names = require('../utils/names'); | ||
var MULTIPLIER_DEFAULT = { | ||
comma: false, | ||
min: 1, | ||
max: 1, | ||
value: '' | ||
}; | ||
@@ -13,4 +19,5 @@ function skipSpaces(node) { | ||
var result = []; | ||
var min = syntaxNode.min === 0 || syntaxNode.min ? syntaxNode.min : 1; | ||
var max = syntaxNode.max === null ? Infinity : (syntaxNode.max || 1); | ||
var multiplier = syntaxNode.multiplier || MULTIPLIER_DEFAULT; | ||
var min = multiplier.min; | ||
var max = multiplier.max === 0 ? Infinity : multiplier.max; | ||
var lastCommaTermCount; | ||
@@ -351,3 +358,3 @@ var lastComma; | ||
if (syntaxNode.comma) { | ||
if (multiplier.comma) { | ||
if (lastComma && lastCommaTermCount === result.length) { | ||
@@ -354,0 +361,0 @@ // nothing match after comma |
var SyntaxParseError = require('./error').SyntaxParseError; | ||
var TAB = 9; | ||
var N = 10; | ||
var F = 12; | ||
var R = 13; | ||
var SPACE = 32; | ||
var EXCLAMATIONMARK = 33; // ! | ||
var NUMBERSIGN = 35; // # | ||
var PERCENTSIGN = 37; // % | ||
var AMPERSAND = 38; // & | ||
var APOSTROPHE = 39; // ' | ||
var LEFTPARENTHESIS = 40; // ( | ||
var RIGHTPARENTHESIS = 41; // ) | ||
var ASTERISK = 42; // * | ||
var PLUSSIGN = 43; // + | ||
var COMMA = 44; // , | ||
var SOLIDUS = 47; // / | ||
var LESSTHANSIGN = 60; // < | ||
var GREATERTHANSIGN = 62; // > | ||
var QUESTIONMARK = 63; // ? | ||
var LEFTSQUAREBRACKET = 91; // [ | ||
var RIGHTSQUAREBRACKET = 93; // ] | ||
var LEFTCURLYBRACKET = 123; // { | ||
var VERTICALLINE = 124; // | | ||
var RIGHTCURLYBRACKET = 125; // } | ||
var COMBINATOR_PRECEDENCE = { | ||
@@ -8,223 +33,234 @@ ' ': 1, | ||
}; | ||
var MULTIPLIER_DEFAULT = { | ||
comma: false, | ||
min: 1, | ||
max: 1 | ||
}; | ||
var MULTIPLIER_ZERO_OR_MORE = { | ||
comma: false, | ||
min: 0, | ||
max: 0 | ||
}; | ||
var MULTIPLIER_ONE_OR_MORE = { | ||
comma: false, | ||
min: 1, | ||
max: 0 | ||
}; | ||
var MULTIPLIER_ONE_OR_MORE_COMMA_SEPARATED = { | ||
comma: true, | ||
min: 1, | ||
max: 0 | ||
}; | ||
var MULTIPLIER_ZERO_OR_ONE = { | ||
comma: false, | ||
min: 0, | ||
max: 1 | ||
}; | ||
var NAME_CHAR = (function() { | ||
var array = typeof Uint32Array === 'function' ? new Uint32Array(128) : new Array(128); | ||
for (var i = 0; i < 128; i++) { | ||
array[i] = /[a-zA-Z0-9\-]/.test(String.fromCharCode(i)) ? 1 : 0; | ||
}; | ||
return array; | ||
})(); | ||
var spaces = /[ \r\n\f]/; | ||
var nameChar = /[a-zA-Z0-9\-]/; | ||
var number = /\d/; | ||
var Scanner = function(str) { | ||
this.str = str; | ||
this.pos = 0; | ||
}; | ||
Scanner.prototype = { | ||
charCode: function() { | ||
return this.pos < this.str.length ? this.str.charCodeAt(this.pos) : 0; | ||
}, | ||
nextCharCode: function() { | ||
return this.pos + 1 < this.str.length ? this.str.charCodeAt(this.pos + 1) : 0; | ||
}, | ||
function readWord(str, start) { | ||
var end = start; | ||
substringToPos: function(end) { | ||
return this.str.substring(this.pos, this.pos = end); | ||
}, | ||
eat: function(code) { | ||
if (this.charCode() !== code) { | ||
error(this, this.pos, 'Expect `' + String.fromCharCode(code) + '`'); | ||
} | ||
while (nameChar.test(str.charAt(end))) { | ||
end++; | ||
this.pos++; | ||
} | ||
}; | ||
return str.substring(start, end); | ||
function scanSpaces(scanner) { | ||
var end = scanner.pos + 1; | ||
for (; end < scanner.str.length; end++) { | ||
var code = scanner.str.charCodeAt(end); | ||
if (code !== R && code !== N && code !== F && code !== SPACE && code !== TAB) { | ||
break; | ||
} | ||
} | ||
return scanner.substringToPos(end); | ||
} | ||
function readNumber(str, start) { | ||
var end = start; | ||
function scanWord(scanner) { | ||
var end = scanner.pos; | ||
while (number.test(str.charAt(end))) { | ||
end++; | ||
for (; end < scanner.str.length; end++) { | ||
var code = scanner.str.charCodeAt(end); | ||
if (code >= 128 || NAME_CHAR[code] === 0) { | ||
break; | ||
} | ||
} | ||
return str.substring(start, end); | ||
if (scanner.pos === end) { | ||
error(scanner, scanner.pos, 'Expect a keyword'); | ||
} | ||
return scanner.substringToPos(end); | ||
} | ||
function readString(str, start) { | ||
var end = start + 1; | ||
function scanNumber(scanner) { | ||
var end = scanner.pos; | ||
while (str.charAt(end) !== '\'') { | ||
end++; | ||
for (; end < scanner.str.length; end++) { | ||
var code = scanner.str.charCodeAt(end); | ||
if (code < 48 || code > 57) { | ||
break; | ||
} | ||
} | ||
end += eat(str, end, '\''); | ||
if (scanner.pos === end) { | ||
error(scanner, scanner.pos, 'Expect a number'); | ||
} | ||
return str.substring(start, end); | ||
return scanner.substringToPos(end); | ||
} | ||
function readMultiplier(str, pos) { | ||
function readRange() { | ||
var min = null; | ||
var max = null; | ||
function scanString(scanner) { | ||
var end = scanner.str.indexOf('\'', scanner.pos + 1); | ||
pos += eat(str, pos, '{'); | ||
if (end === -1) { | ||
error(scanner, scanner.str.length, 'Expect a quote'); | ||
} | ||
min = readNumber(str, pos) || error(str, pos); | ||
pos += min.length; | ||
return scanner.substringToPos(end + 1); | ||
} | ||
if (str.charAt(pos) === ',') { | ||
pos++; | ||
if (str.charAt(pos) !== '}') { | ||
max = readNumber(str, pos) || error(str, pos); | ||
pos += max.length; | ||
} | ||
} else { | ||
max = min; | ||
} | ||
function readMultiplierRange(scanner, comma) { | ||
var min = null; | ||
var max = null; | ||
pos += eat(str, pos, '}'); | ||
scanner.eat(LEFTCURLYBRACKET); | ||
return { | ||
min: Number(min), | ||
max: max ? Number(max) : null | ||
}; | ||
min = scanNumber(scanner); | ||
if (scanner.charCode() === COMMA) { | ||
scanner.pos++; | ||
if (scanner.charCode() !== RIGHTCURLYBRACKET) { | ||
max = scanNumber(scanner); | ||
} | ||
} else { | ||
max = min; | ||
} | ||
switch (str.charAt(pos)) { | ||
case '*': | ||
return { | ||
comma: false, | ||
min: 0, | ||
max: null, | ||
value: '*' | ||
}; | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
case '+': | ||
return { | ||
comma: false, | ||
min: 1, | ||
max: null, | ||
value: '+' | ||
}; | ||
return { | ||
comma: comma, | ||
min: Number(min), | ||
max: max ? Number(max) : 0 | ||
}; | ||
} | ||
case '?': | ||
return { | ||
comma: false, | ||
min: 0, | ||
max: 1, | ||
value: '?' | ||
}; | ||
function readMultiplier(scanner) { | ||
switch (scanner.charCode()) { | ||
case ASTERISK: | ||
scanner.pos++; | ||
return MULTIPLIER_ZERO_OR_MORE; | ||
case '#': | ||
var start = pos; | ||
case PLUSSIGN: | ||
scanner.pos++; | ||
return MULTIPLIER_ONE_OR_MORE; | ||
pos++; // # | ||
case QUESTIONMARK: | ||
scanner.pos++; | ||
return MULTIPLIER_ZERO_OR_ONE; | ||
if (str.charAt(pos) === '{') { | ||
var range = readRange(); | ||
if (range) { | ||
return { | ||
comma: true, | ||
min: range.min, | ||
max: range.max, | ||
value: str.substring(start, pos) | ||
}; | ||
} | ||
case NUMBERSIGN: | ||
scanner.pos++; | ||
if (scanner.charCode() !== LEFTCURLYBRACKET) { | ||
return MULTIPLIER_ONE_OR_MORE_COMMA_SEPARATED; | ||
} | ||
return { | ||
comma: true, | ||
min: 1, | ||
max: null, | ||
value: '#' | ||
}; | ||
return readMultiplierRange(scanner, true); | ||
case '{': | ||
var start = pos; | ||
var range = readRange(); | ||
if (range) { | ||
return { | ||
comma: false, | ||
min: range.min, | ||
max: range.max, | ||
value: str.substring(start, pos) | ||
}; | ||
} | ||
break; | ||
case LEFTCURLYBRACKET: | ||
return readMultiplierRange(scanner, false); | ||
} | ||
return { | ||
comma: false, | ||
min: 1, | ||
max: 1, | ||
value: '' | ||
}; | ||
return MULTIPLIER_DEFAULT; | ||
} | ||
function readProperty(str, start) { | ||
var end = start; | ||
var multiplier; | ||
function readProperty(scanner) { | ||
var name; | ||
end += eat(str, end, '<'); | ||
end += eat(str, end, '\''); | ||
scanner.eat(LESSTHANSIGN); | ||
scanner.eat(APOSTROPHE); | ||
name = readWord(str, end) || error(str, end); | ||
end += name.length; | ||
name = scanWord(scanner); | ||
end += eat(str, end, '\''); | ||
end += eat(str, end, '>'); | ||
scanner.eat(APOSTROPHE); | ||
scanner.eat(GREATERTHANSIGN); | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
return { | ||
type: 'Property', | ||
name: name, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
} | ||
function readType(str, start) { | ||
var end = start; | ||
var multiplier; | ||
function readType(scanner) { | ||
var name; | ||
end += eat(str, end, '<'); | ||
scanner.eat(LESSTHANSIGN); | ||
name = scanWord(scanner); | ||
name = readWord(str, end) || error(str, end); | ||
end += name.length; | ||
if (str.charAt(end) === '(' && str.charAt(end + 1) === ')') { | ||
if (scanner.charCode() === LEFTPARENTHESIS && | ||
scanner.nextCharCode() === RIGHTPARENTHESIS) { | ||
scanner.pos += 2; | ||
name += '()'; | ||
end += 2; | ||
} | ||
end += eat(str, end, '>'); | ||
scanner.eat(GREATERTHANSIGN); | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
return { | ||
type: 'Type', | ||
name: name, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
} | ||
function readKeywordOrFunction(str, start) { | ||
var end = start; | ||
function readKeywordOrFunction(scanner) { | ||
var sequence = null; | ||
var multiplier; | ||
var name; | ||
name = readWord(str, end) || error(str, end); | ||
end += name.length; | ||
name = scanWord(scanner); | ||
if (str.charAt(end) === '(') { | ||
sequence = readSequence(str, end + 1); | ||
end += sequence.value.length + 1; | ||
if (scanner.charCode() === LEFTPARENTHESIS) { | ||
scanner.pos++; | ||
sequence = readSequence(scanner); | ||
scanner.eat(RIGHTPARENTHESIS); | ||
end += eat(str, end, ')'); | ||
return { | ||
type: 'Function', | ||
name: name, | ||
sequence: sequence, | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
} | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
return { | ||
type: sequence ? 'Function' : 'Keyword', | ||
type: 'Keyword', | ||
name: name, | ||
sequence: sequence, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
@@ -283,19 +319,19 @@ } | ||
function readSequence(str, start) { | ||
function readSequence(scanner) { | ||
var terms = []; | ||
var combinators = {}; | ||
var end = start; | ||
var token; | ||
var lastToken; | ||
var prevToken = null; | ||
var prevTokenPos = scanner.pos; | ||
while (token = peek(str, end)) { | ||
while (token = peek(scanner)) { | ||
if (token.type !== 'Spaces') { | ||
if (token.type === 'Combinator') { | ||
// check for combinator in group beginning and double combinator sequence | ||
if (!lastToken || lastToken.type === 'Combinator') { | ||
error(str, end); | ||
if (prevToken === null || prevToken.type === 'Combinator') { | ||
error(scanner, prevTokenPos, 'Unexpected combinator'); | ||
} | ||
combinators[token.value] = true; | ||
} else if (lastToken && lastToken.type !== 'Combinator') { | ||
} else if (prevToken !== null && prevToken.type !== 'Combinator') { | ||
combinators[' '] = true; // a b | ||
@@ -309,11 +345,10 @@ terms.push({ | ||
terms.push(token); | ||
lastToken = token; | ||
prevToken = token; | ||
prevTokenPos = scanner.pos; | ||
} | ||
end += token.value.length; | ||
} | ||
// check for combinator in group ending | ||
if (lastToken && lastToken.type === 'Combinator') { | ||
error(str, end - lastToken.value.length); | ||
if (prevToken !== null && prevToken.type === 'Combinator') { | ||
error(scanner, scanner.pos - prevTokenPos, 'Unexpected combinator'); | ||
} | ||
@@ -324,25 +359,19 @@ | ||
terms: terms, | ||
combinator: regroupTerms(terms, combinators) || ' ', | ||
value: str.substring(start, end) | ||
combinator: regroupTerms(terms, combinators) || ' ' | ||
}; | ||
} | ||
function readGroup(str, start) { | ||
function readGroup(scanner) { | ||
var sequence; | ||
var nonEmpty = false; | ||
var multiplier; | ||
var end = start; | ||
end += eat(str, end, '['); | ||
scanner.eat(LEFTSQUAREBRACKET); | ||
sequence = readSequence(scanner); | ||
sequence = readSequence(str, end); | ||
end += sequence.value.length; | ||
scanner.eat(RIGHTSQUAREBRACKET); | ||
multiplier = readMultiplier(scanner); | ||
end += eat(str, end, ']'); | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
if (str.charAt(end) === '!') { | ||
end++; | ||
if (scanner.charCode() === EXCLAMATIONMARK) { | ||
scanner.pos++; | ||
nonEmpty = true; | ||
@@ -356,60 +385,40 @@ } | ||
nonEmpty: nonEmpty, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: multiplier | ||
}; | ||
} | ||
function readSpaces(str, start) { | ||
var end = start + 1; | ||
while (spaces.test(str.charAt(end))) { | ||
end++; | ||
} | ||
return { | ||
type: 'Spaces', | ||
value: str.substring(start, end) | ||
}; | ||
} | ||
function peek(scanner) { | ||
var code = scanner.charCode(); | ||
function peek(str, start) { | ||
var c = str.charAt(start); | ||
var end = start + 1; | ||
if (spaces.test(c)) { | ||
return readSpaces(str, start); | ||
if (code < 128 && NAME_CHAR[code] === 1) { | ||
return readKeywordOrFunction(scanner); | ||
} | ||
if (nameChar.test(c)) { | ||
return readKeywordOrFunction(str, start); | ||
} | ||
switch (code) { | ||
case LEFTSQUAREBRACKET: | ||
return readGroup(scanner); | ||
switch (c) { | ||
case '[': | ||
return readGroup(str, start); | ||
case '<': | ||
if (str.charAt(start + 1) === '\'') { | ||
return readProperty(str, start); | ||
case LESSTHANSIGN: | ||
if (scanner.nextCharCode() === APOSTROPHE) { | ||
return readProperty(scanner); | ||
} else { | ||
return readType(str, start); | ||
return readType(scanner); | ||
} | ||
case '|': | ||
end += str.charAt(end) === '|'; | ||
case VERTICALLINE: | ||
return { | ||
type: 'Combinator', | ||
value: str.substring(start, end) | ||
value: scanner.substringToPos(scanner.nextCharCode() === VERTICALLINE ? scanner.pos + 2 : scanner.pos + 1) | ||
}; | ||
case '&': | ||
if (str.charAt(end) === '&') { | ||
return { | ||
type: 'Combinator', | ||
value: '&&' | ||
}; | ||
} | ||
break; | ||
case AMPERSAND: | ||
scanner.pos++; | ||
scanner.eat(AMPERSAND); | ||
return { | ||
type: 'Combinator', | ||
value: '&&' | ||
}; | ||
case ',': | ||
case COMMA: | ||
scanner.pos++; | ||
return { | ||
@@ -420,3 +429,4 @@ type: 'Comma', | ||
case '/': | ||
case SOLIDUS: | ||
scanner.pos++; | ||
return { | ||
@@ -427,3 +437,4 @@ type: 'Slash', | ||
case '%': // looks like exception, needs for attr()'s <type-or-unit> | ||
case PERCENTSIGN: // looks like exception, needs for attr()'s <type-or-unit> | ||
scanner.pos++; | ||
return { | ||
@@ -434,38 +445,40 @@ type: 'Percent', | ||
case '(': | ||
var sequence = readSequence(str, end); | ||
end += sequence.value.length; | ||
end += eat(str, end, ')'); | ||
case LEFTPARENTHESIS: | ||
scanner.pos++; | ||
var sequence = readSequence(scanner); | ||
scanner.eat(RIGHTPARENTHESIS); | ||
return { | ||
type: 'Parentheses', | ||
sequence: sequence, | ||
value: str.substring(start, end) | ||
sequence: sequence | ||
}; | ||
case '\'': | ||
case APOSTROPHE: | ||
return { | ||
type: 'String', | ||
value: readString(str, start) | ||
value: scanString(scanner) | ||
}; | ||
} | ||
} | ||
function eat(str, pos, ch) { | ||
if (str.charAt(pos) !== ch) { | ||
error(str, pos, 'Expect `' + ch + '`'); | ||
case SPACE: | ||
case TAB: | ||
case N: | ||
case R: | ||
case F: | ||
return { | ||
type: 'Spaces', | ||
value: scanSpaces(scanner) | ||
}; | ||
} | ||
return 1; | ||
} | ||
function error(str, pos, msg) { | ||
throw new SyntaxParseError(msg || 'Unexpected input', str, pos); | ||
function error(scanner, pos, msg) { | ||
throw new SyntaxParseError(msg || 'Unexpected input', scanner.str, pos); | ||
} | ||
module.exports = function parse(str) { | ||
var result = readSequence(str, 0); | ||
function parse(str) { | ||
var scanner = new Scanner(str); | ||
var result = readSequence(scanner); | ||
if (result.value !== str) { | ||
error(str, result.value.length); | ||
if (scanner.pos !== str.length) { | ||
error(scanner, scanner.pos); | ||
} | ||
@@ -479,2 +492,8 @@ | ||
return result; | ||
}; | ||
} | ||
// warm up parse to elimitate code branches that never execute | ||
// fix soft deoptimizations (insufficient type feedback) | ||
parse('[a&&<b>#|<\'c\'>*||e(){2,} f{2} /,(% g#{1,2})]!'); | ||
module.exports = parse; |
var MatchError = require('./error').MatchError; | ||
var walkAst = require('../utils/walk').all; | ||
var names = require('../utils/names'); | ||
var generic = require('./generic'); | ||
var parse = require('./parse'); | ||
var stringify = require('./stringify'); | ||
var translate = require('./translate'); | ||
var walk = require('./walk'); | ||
@@ -16,3 +17,3 @@ var match = require('./match'); | ||
if (map[name].syntax) { | ||
result[name] = syntaxAsAst ? map[name].syntax : stringify(map[name].syntax); | ||
result[name] = syntaxAsAst ? map[name].syntax : translate(map[name].syntax); | ||
} | ||
@@ -28,5 +29,26 @@ } | ||
function valueHasVar(value) { | ||
var hasVar = false; | ||
walkAst(value, function(node) { | ||
if (node.type === 'Function' && node.name.toLowerCase() === 'var') { | ||
hasVar = true; | ||
} | ||
}); | ||
return hasVar; | ||
} | ||
function matchSyntax(dictionary, syntax, value) { | ||
var result = match(dictionary, dictionary.valueCommonSyntax, value.sequence.head); | ||
var result; | ||
if (valueHasVar(value)) { | ||
return { | ||
type: 'NoMatch', | ||
comment: 'Due to matching for value with var() is very complex those values are always valid for now' | ||
}; | ||
} | ||
result = match(dictionary, dictionary.valueCommonSyntax, value.sequence.head); | ||
if (!result.match) { | ||
@@ -83,2 +105,3 @@ result = syntax.match(value.sequence.head); | ||
createDescriptor: function(syntax) { | ||
var self = this; | ||
var descriptor = { | ||
@@ -90,6 +113,32 @@ syntax: null, | ||
if (typeof syntax === 'function') { | ||
descriptor.match = syntax; | ||
descriptor.match = function(node) { | ||
if (node && syntax(node)) { | ||
return { | ||
badNode: null, | ||
lastNode: null, | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return null; | ||
}; | ||
} else { | ||
descriptor.syntax = typeof syntax === 'string' ? parse(syntax) : syntax; | ||
descriptor.match = match.bind(null, this, descriptor.syntax); | ||
if (typeof syntax === 'string') { | ||
Object.defineProperty(descriptor, 'syntax', { | ||
get: function() { | ||
Object.defineProperty(descriptor, 'syntax', { | ||
value: parse(syntax) | ||
}); | ||
return descriptor.syntax; | ||
} | ||
}); | ||
} else { | ||
descriptor.syntax = syntax; | ||
} | ||
descriptor.match = function(ast) { | ||
return match(self, descriptor.syntax, ast); | ||
}; | ||
} | ||
@@ -96,0 +145,0 @@ |
@@ -30,18 +30,16 @@ // | ||
Object.defineProperty(List.prototype, 'size', { | ||
get: function() { | ||
var size = 0; | ||
var cursor = this.head; | ||
List.createItem = createItem; | ||
List.prototype.createItem = createItem; | ||
while (cursor) { | ||
size++; | ||
cursor = cursor.next; | ||
} | ||
List.prototype.getSize = function() { | ||
var size = 0; | ||
var cursor = this.head; | ||
return size; | ||
while (cursor) { | ||
size++; | ||
cursor = cursor.next; | ||
} | ||
}); | ||
List.createItem = createItem; | ||
List.prototype.createItem = createItem; | ||
return size; | ||
}; | ||
@@ -82,6 +80,5 @@ List.prototype.fromArray = function(array) { | ||
}; | ||
List.prototype.toJSON = function() { | ||
return this.toArray(); | ||
}; | ||
List.prototype.toJSON = List.prototype.toArray; | ||
List.prototype.isEmpty = function() { | ||
@@ -88,0 +85,0 @@ return this.head === null; |
@@ -153,3 +153,3 @@ function each(list) { | ||
case 'Progid': | ||
return translate(node.value); | ||
return node.value; | ||
@@ -156,0 +156,0 @@ case 'Combinator': |
@@ -227,3 +227,3 @@ var SourceMapGenerator = require('source-map').SourceMapGenerator; | ||
case 'Progid': | ||
return translate(node.value); | ||
return node.value; | ||
@@ -230,0 +230,0 @@ case 'Combinator': |
@@ -175,3 +175,2 @@ function walkRules(node, item, list) { | ||
case 'Url': | ||
case 'Progid': | ||
walkAll.call(this, node.value); | ||
@@ -181,2 +180,3 @@ break; | ||
// nothig to do with | ||
// case 'Progid': | ||
// case 'Property': | ||
@@ -183,0 +183,0 @@ // case 'Combinator': |
{ | ||
"name": "css-tree", | ||
"version": "1.0.0-alpha7", | ||
"version": "1.0.0-alpha8", | ||
"description": "Detailed CSS parser", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -0,1 +1,7 @@ | ||
<img align="right" width="111" height="111" | ||
alt="CSSTree logo" | ||
src="https://cloud.githubusercontent.com/assets/270491/19243723/6f9136c6-8f21-11e6-82ac-eeeee4c6c452.png"/> | ||
# CSSTree | ||
[![NPM version](https://img.shields.io/npm/v/css-tree.svg)](https://www.npmjs.com/package/css-tree) | ||
@@ -7,6 +13,12 @@ [![Build Status](https://travis-ci.org/csstree/csstree.svg?branch=master)](https://travis-ci.org/csstree/csstree) | ||
Fast detailed CSS parser | ||
> Work in progress | ||
Docs and tools: | ||
* [CSS syntax reference](https://csstree.github.io/docs/syntax.html) | ||
* [CSS syntax validator](https://csstree.github.io/docs/validator.html) | ||
Related: | ||
Related projects: | ||
@@ -18,4 +30,4 @@ * [csstree-validator](https://github.com/csstree/validator) – NPM package to validate CSS | ||
* [Sublime plugin](https://github.com/csstree/SublimeLinter-contrib-csstree) | ||
* [VS Code plugin](https://github.com/csstree/vscode-csstree) | ||
* [Atom plugin](https://github.com/csstree/atom-csstree-validator) | ||
* [VS Code plugin](https://github.com/csstree/vscode-plugin) | ||
* [Atom plugin](https://github.com/csstree/atom-plugin) | ||
@@ -191,2 +203,2 @@ ## Install | ||
[Template:CSSData](https://developer.mozilla.org/en-US/docs/Template:CSSData) by Mozilla Contributors is licensed under [CC-BY-SA 2.5](http://creativecommons.org/licenses/by-sa/2.5/) | ||
Syntax matching use [mdn/data](https://github.com/mdn/data) dictionaries by Mozilla Contributors |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
33
202
526790
11989
1