Skip to content

Commit

Permalink
implement parser options to specify what should be parsed in details …
Browse files Browse the repository at this point in the history
…(at-rule expression, selector, value, custom property)

- implement `parseAtruleExpression`, `parseSelector`, `parseValue`,
`parseCustomProperty` parser options to specify what to parse and what
don't; all is true by default except `parseCustomProperty` (it's false
by default)
- allow to set parse options in test
  • Loading branch information
lahmatiy committed Feb 5, 2017
1 parent 01c3e59 commit 43e8193
Show file tree
Hide file tree
Showing 20 changed files with 279 additions and 92 deletions.
14 changes: 13 additions & 1 deletion lib/parser/Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,19 @@ function readSC() {

var Parser = function() {
this.scanner = new Tokenizer();
this.needPositions = false;
this.filename = '<unknown>';
this.needPositions = false;
};

Parser.prototype = {
scanner: null,
filename: '<unknown>',
needPositions: false,
parseAtruleExpression: true,
parseSelector: true,
parseValue: true,
parseCustomProperty: false,

SPACE_NODE: Object.freeze({ type: 'Space' }),

scopeAtruleExpression: {},
Expand Down Expand Up @@ -190,6 +198,10 @@ Parser.prototype = {
this.scanner.setSource(source, options.line, options.column);
this.filename = options.filename || '<unknown>';
this.needPositions = Boolean(options.positions);
this.parseAtruleExpression = 'parseAtruleExpression' in options ? Boolean(options.parseAtruleExpression) : true;
this.parseSelector = 'parseSelector' in options ? Boolean(options.parseSelector) : true;
this.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true;
this.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false;

switch (context) {
case 'value':
Expand Down
9 changes: 7 additions & 2 deletions lib/parser/type/Atrule.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var COMMERCIALAT = TYPE.CommercialAt;
var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
var RIGHTCURLYBRACKET = TYPE.RightCurlyBracket;
var DISALLOW_VAR = false;
var BALANCED = true;

function isBlockAtrule() {
for (var offset = 1, type; type = this.scanner.lookupType(offset); offset++) {
Expand Down Expand Up @@ -35,8 +36,12 @@ module.exports = function Atrule() {
nameLowerCase = name.toLowerCase();
this.readSC();

expression = this.AtruleExpression(name);
this.readSC();
if (this.parseAtruleExpression) {
expression = this.AtruleExpression(name);
this.readSC();
} else {
expression = this.Raw(BALANCED, SEMICOLON, LEFTCURLYBRACKET);
}

if (this.atrule.hasOwnProperty(nameLowerCase)) {
if (typeof this.atrule[nameLowerCase].block === 'function') {
Expand Down
14 changes: 9 additions & 5 deletions lib/parser/type/Declaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ function getImportant() {
return true;
}

function isCustomProperty(name) {
return name.length >= 2 &&
name.charCodeAt(0) === HYPHENMINUS &&
name.charCodeAt(1) === HYPHENMINUS;
}

module.exports = function Declaration() {
var start = this.scanner.tokenStart;
var property = readProperty.call(this);
Expand All @@ -49,12 +55,10 @@ module.exports = function Declaration() {
this.readSC();
this.scanner.eat(COLON);

if (property.length >= 2 &&
property.charCodeAt(0) === HYPHENMINUS &&
property.charCodeAt(1) === HYPHENMINUS) {
value = this.Raw(BALANCED, SEMICOLON, EXCLAMATIONMARK);
} else {
if (isCustomProperty(property) ? this.parseCustomProperty : this.parseValue) {
value = this.Value(property);
} else {
value = this.Raw(BALANCED, SEMICOLON, EXCLAMATIONMARK);
}

if (this.scanner.tokenType === EXCLAMATIONMARK) {
Expand Down
16 changes: 9 additions & 7 deletions lib/parser/type/Raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ module.exports = function Raw(balanced, endTokenType1, endTokenType2) {
if (balanced) {
scan:
for (; !this.scanner.eof; this.scanner.next()) {
if (stack.length === 0) {
if (this.scanner.tokenType === endTokenType1 ||
this.scanner.tokenType === endTokenType2) {
if (stack.length === 0) {
break scan;
}
}
}

switch (this.scanner.tokenType) {
case popType:
if (stack.length === 0) {
Expand Down Expand Up @@ -46,13 +55,6 @@ module.exports = function Raw(balanced, endTokenType1, endTokenType2) {
stack.push(popType);
popType = RIGHTSQUAREBRACKET;
break;

case endTokenType1:
case endTokenType2:
if (stack.length === 0) {
break scan;
}
break;
}
}
} else {
Expand Down
10 changes: 7 additions & 3 deletions lib/parser/type/SelectorList.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
var List = require('../../utils/list');
var COMMA = require('../../tokenizer').TYPE.Comma;
var TYPE = require('../../tokenizer').TYPE;

var COMMA = TYPE.Comma;
var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
var BALANCED = true;

module.exports = function SelectorList(relative) {
this.readSC();
Expand All @@ -10,11 +14,11 @@ module.exports = function SelectorList(relative) {
var selector = null;

while (!this.scanner.eof) {
selector = this.Selector(relative);
selector = this.parseSelector ? this.Selector(relative) : this.Raw(BALANCED, COMMA, LEFTCURLYBRACKET);
children.appendData(selector);

if (this.needPositions) {
end = selector.children.last().loc.end.offset;
end = selector.loc.end.offset;
}

if (this.scanner.tokenType === COMMA) {
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function eachDelim(list, delimeter) {
function translateAtRule(node) {
var result = '@' + node.name;

if (node.expression && !node.expression.children.isEmpty()) {
if (node.expression !== null) {
result += ' ' + translate(node.expression);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/utils/translateWithSourceMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function translate(node) {
case 'Atrule':
var nodes = ['@', node.name];

if (node.expression && !node.expression.children.isEmpty()) {
if (node.expression !== null) {
nodes.push(' ', translate(node.expression));
}

Expand Down
18 changes: 18 additions & 0 deletions test/fixture/parse/atrule/atrule/media.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,24 @@
}
}
},
"shouldn't parse an at-rule expression when parseAtruleExpression is false": {
"options": {
"parseAtruleExpression": false
},
"source": "@media screen and (feature: 1) {}",
"ast": {
"type": "Atrule",
"name": "media",
"expression": {
"type": "Raw",
"value": "screen and (feature: 1) "
},
"block": {
"type": "Block",
"children": []
}
}
},
"error": [
{
"source": "@media;",
Expand Down
15 changes: 15 additions & 0 deletions test/fixture/parse/atrule/simple.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@
"block": null
}
},
"shouldn't parse a atrule expression when parseAtruleExpression is false": {
"options": {
"parseAtruleExpression": false
},
"source": "@test a b;",
"ast": {
"type": "Atrule",
"name": "test",
"expression": {
"type": "Raw",
"value": "a b"
},
"block": null
}
},
"error": [
{
"source": "@atrule!;",
Expand Down
15 changes: 15 additions & 0 deletions test/fixture/parse/declaration/Declaration.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,20 @@
]
}
}
},
"shouldn't parse a value when parseValue is false": {
"options": {
"parseValue": false
},
"source": "property: value",
"ast": {
"type": "Declaration",
"important": false,
"property": "property",
"value": {
"type": "Raw",
"value": " value"
}
}
}
}
27 changes: 27 additions & 0 deletions test/fixture/parse/declaration/custom-property.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,33 @@
}
}
},
"should parse a custom property value when parseCustomProperty is true": {
"options": {
"parseCustomProperty": true
},
"source": "--var:1 foo",
"ast": {
"type": "Declaration",
"important": false,
"property": "--var",
"value": {
"type": "Value",
"children": [
{
"type": "Number",
"value": "1"
},
{
"type": "Space"
},
{
"type": "Identifier",
"name": "foo"
}
]
}
}
},
"error": [
{
"source": "--var: ([)]",
Expand Down
7 changes: 7 additions & 0 deletions test/fixture/parse/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var fs = require('fs');
var path = require('path');
var JsonLocator = require('../../helpers/JsonLocator.js');
var merge = require('../../helpers').merge;
var wrapper = {
rule: function(ast) {
return {
Expand Down Expand Up @@ -84,10 +85,16 @@ var tests = fs.readdirSync(__dirname).reduce(function(result, scope) {
if (Array.isArray(origTests[key])) {
origTests[key].forEach(function(test, idx) {
test.name = locator.get(key, idx);
test.options = merge(test.options, {
context: scope
});
processTest(test, key, key + '#' + (idx + 1));
});
} else {
origTests[key].name = locator.get(key);
origTests[key].options = merge(origTests[key].options, {
context: scope
});
processTest(origTests[key], key, key);
}
}
Expand Down
42 changes: 42 additions & 0 deletions test/fixture/parse/rule/Rule.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,5 +991,47 @@
]
}
}
},
"shouldn't parse a selector when parseSelector is false": {
"options": {
"parseSelector": false
},
"source": ".foo, a:not(a = b) { foo: 1; }",
"translate": ".foo, a:not(a = b) {foo:1}",
"ast": {
"type": "Rule",
"selector": {
"type": "SelectorList",
"children": [
{
"type": "Raw",
"value": ".foo"
},
{
"type": "Raw",
"value": " a:not(a = b) "
}
]
},
"block": {
"type": "Block",
"children": [
{
"type": "Declaration",
"important": false,
"property": "foo",
"value": {
"type": "Value",
"children": [
{
"type": "Number",
"value": "1"
}
]
}
}
]
}
}
}
}
Loading

0 comments on commit 43e8193

Please sign in to comment.