Skip to content

Instantly share code, notes, and snippets.

@lahmatiy
Created September 5, 2017 16:31
Show Gist options
  • Save lahmatiy/8bd89a7f2a4b9205157d0b807f0d170e to your computer and use it in GitHub Desktop.
Save lahmatiy/8bd89a7f2a4b9205157d0b807f0d170e to your computer and use it in GitHub Desktop.
function isSelector(node) {
return node.type === 'TypeSelector' ||
node.type === 'IdSelector' ||
node.type === 'ClassSelector' ||
node.type === 'AttributeSelector' ||
node.type === 'PseudoClassSelector' ||
node.type === 'PseudoElementSelector';
}
function isLegacyPseudoElement(node) {
var name = node.name.toLowerCase();
return name === 'before' ||
name === 'after' ||
name === 'first-letter' ||
name === 'first-line';
}
function validateSelector(selector, allowRelativeSelector) {
var lastCombinator = null;
var lastPseudoElement = null;
var lastSimpleSelector = null;
selector.each(function(node, item) {
if (node.type === 'Combinator') {
if (lastSimpleSelector !== null && !allowRelativeSelector) {
throw new Error('Selector can\'t starts with a combinator');
}
if (lastCombinator !== null && lastCombinator.type !== 'WhiteSpace') {
throw new Error('Combinator can\'t be followed by a combinator');
}
lastCombinator = node;
return;
}
if (node.type === 'WhiteSpace') {
if (lastSimpleSelector !== null && lastCombinator === null) {
lastCombinator = node;
}
return;
}
lastCombinator = null;
lastSimpleSelector = node;
switch (node.type) {
case 'TypeSelector':
if (item.prev !== null && isSelector(item.prev.data)) {
throw new Error('A compound selector can contain at most one type selector, and if it does, that must be the first simple selector in it');
}
if (lastPseudoElement !== null) {
throw new Error(node.type + ' can\'t be used after pseudo-element');
}
break;
case 'IdSelector':
case 'ClassSelector':
case 'AttributeSelector':
if (lastPseudoElement !== null) {
throw new Error(node.type + ' can\'t be used after pseudo-element');
}
break;
case 'PseudoClassSelector':
if (isLegacyPseudoElement(node.name)) {
lastPseudoElement = node;
}
break;
case 'PseudoElementSelector':
lastPseudoElement = node;
break;
default:
throw new Error('Unexpected node type: ' + node.type);
}
});
if (lastCombinator !== null) {
throw new Error('Trailing combinator');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment