Skip to content

Commit

Permalink
extend Declaration validator to check for known property name
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Oct 11, 2017
1 parent e0ae0aa commit 73f0ee7
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 56 deletions.
122 changes: 66 additions & 56 deletions lib/syntax/node/Declaration.js
@@ -1,5 +1,5 @@
var resolveProperty = require('../../utils/names').property;
var TYPE = require('../../tokenizer').TYPE;

var IDENTIFIER = TYPE.Identifier;
var COLON = TYPE.Colon;
var EXCLAMATIONMARK = TYPE.ExclamationMark;
Expand All @@ -11,9 +11,60 @@ var SEMICOLON = TYPE.Semicolon;
var PLUSSIGN = TYPE.PlusSign;
var NUMBERSIGN = TYPE.NumberSign;

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

function consumeProperty() {
var start = this.scanner.tokenStart;
var prefix = 0;

// hacks
switch (this.scanner.tokenType) {
case ASTERISK:
case DOLLARSIGN:
case PLUSSIGN:
case NUMBERSIGN:
prefix = 1;
break;

// TODO: not sure we should support this hack
case SOLIDUS:
prefix = this.scanner.lookupType(1) === SOLIDUS ? 2 : 1;
break;
}

if (this.scanner.lookupType(prefix) === HYPHENMINUS) {
prefix++;
}

if (prefix) {
this.scanner.skip(prefix);
}

this.scanner.eat(IDENTIFIER);

return this.scanner.substrToCursor(start);
}

// ! sc* important
function consumeImportant(scanner) {
scanner.eat(EXCLAMATIONMARK);
scanner.skipSC();

var important = scanner.consume(IDENTIFIER);

// store original value in case it differ from `important`
// for better original source restoring and hacks like `!ie` support
return important === 'important' ? true : important;
}

function consumeValueRaw(startToken) {
return this.Raw(startToken, EXCLAMATIONMARK, SEMICOLON, false, true);
}

function consumeCustomPropertyRaw(startToken) {
return this.Raw(startToken, EXCLAMATIONMARK, SEMICOLON, false, false);
}
Expand All @@ -29,7 +80,7 @@ module.exports = {
parse: function() {
var start = this.scanner.tokenStart;
var startToken = this.scanner.currentToken;
var property = readProperty.call(this);
var property = consumeProperty.call(this);
var customProperty = isCustomProperty(property);
var parseValue = customProperty ? this.parseCustomProperty : this.parseValue;
var consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
Expand Down Expand Up @@ -60,7 +111,7 @@ module.exports = {
}

if (this.scanner.tokenType === EXCLAMATIONMARK) {
important = getImportant(this.scanner);
important = consumeImportant(this.scanner);
this.scanner.skipSC();
}

Expand Down Expand Up @@ -95,59 +146,18 @@ module.exports = {
}
},
validate: function(node, warn) {
var match = this.matchDeclaration(node);
if (match.error) {
warn(node, match.error);
var property = resolveProperty(node.property);
var name = node.property.substr(property.hack.length); // TODO: replace for property.name, see https://github.com/csstree/csstree/issues/63
if (name.toLowerCase() in this.defs.Declaration === false) {
warn(node, new SyntaxError('Unknown property `' + name + '`'));
} else {
var match = this.matchDeclaration(node);
if (match.error) {
if (match.error.name === 'SyntaxMatchError' &&
match.error.name === 'SyntaxReferenceError') {
warn(node, match.error);
}
}
}
}
};

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

function readProperty() {
var start = this.scanner.tokenStart;
var prefix = 0;

// hacks
switch (this.scanner.tokenType) {
case ASTERISK:
case DOLLARSIGN:
case PLUSSIGN:
case NUMBERSIGN:
prefix = 1;
break;

// TODO: not sure we should support this hack
case SOLIDUS:
prefix = this.scanner.lookupType(1) === SOLIDUS ? 2 : 1;
break;
}

if (this.scanner.lookupType(prefix) === HYPHENMINUS) {
prefix++;
}

if (prefix) {
this.scanner.skip(prefix);
}

this.scanner.eat(IDENTIFIER);

return this.scanner.substrToCursor(start);
}

// ! ws* important
function getImportant(scanner) {
scanner.eat(EXCLAMATIONMARK);
scanner.skipSC();

var important = scanner.consume(IDENTIFIER);

// store original value in case it differ from `important`
// for better original source restoring and hacks like `!ie` support
return important === 'important' ? true : important;
}
13 changes: 13 additions & 0 deletions test/lexer.js
Expand Up @@ -275,6 +275,19 @@ describe('lexer', function() {
assert.equal(errors[0].node, ast.children.last());
assert.equal(errors[0].error.message, 'Unknown unit `toString`');
});

it('Declaration', function() {
// using toString as bad name we check 2 things: bad name matching and
// false positive matching b/c of wrong search for name existance in dict
var ast = parseCss('color: red; Color: red; //color: red; //-vendor-color: red; toString: red', { context: 'declarationList' });
var errors = syntax.lexer.checkValidity(ast);

assert.equal(errors.length, 2);
assert.equal(errors[0].node, ast.children.tail.prev.data);
assert.equal(errors[0].error.message, 'Unknown property `-vendor-color`');
assert.equal(errors[1].node, ast.children.last());
assert.equal(errors[1].error.message, 'Unknown property `toString`');
});
});

describe('matchProperty()', function() {
Expand Down

0 comments on commit 73f0ee7

Please sign in to comment.