8000 Fix interpolation in placeholders - bem depth by DanPurdy · Pull Request #818 · sasstools/sass-lint · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix interpolation in placeholders - bem depth #818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions lib/rules/bem-depth.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

var helpers = require('../helpers');
var selectorHelpers = require('../selector-helpers');

/**
* Get number of BEM elements in
Expand Down Expand Up @@ -32,18 +33,19 @@ module.exports = {
maxDepth = parser.options['max-depth'];

if (node.is('placeholder')) {
name = node.first('ident') && node.first('ident').content;
depth = bemDepth(name);

if (name && depth > maxDepth) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': node.start.line,
'column': node.start.column,
'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ',
depth, ' were found.'].join(''),
'severity': parser.severity
});
name = selectorHelpers.constructSelector(node);
if (name) {
depth = bemDepth(name);
if (depth > maxDepth) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': node.start.line,
'column': node.start.column,
'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ',
depth, ' were found.'].join(''),
'severity': parser.severity
});
}
}
}
else {
Expand Down
132 changes: 132 additions & 0 deletions lib/selector-helpers.js
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
'use strict';

// ==============================================================================
// Helpers
// ==============================================================================

var simpleIdents = [
'ident',
'number',
'operator',
'combinator',
'string',
'parentSelector',
'delimiter',
'typeSelector',
'attributeMatch'
];

/**
* Adds grammar around our content blocks to construct selectors with
* more readable formats.
*
* @param {object} val - The current value node
* @param {string} prefix - The grammar to prefix the value with
* @param {string} suffix - The grammar to add after the value
* @returns {string} The correct readable format
*/
var addGrammar = function (val, prefix, suffix) {
return prefix + val.content + suffix;
};

/**
* Adds grammar around our content blocks to construct selectors with
* more readable formats and loops the content as they're within sub blocks.
*
* @param {object} val - The current value node
* @param {string} prefix - The grammar to prefix the value with
* @param {string} suffix - The grammar to add after the value
* @param {function} constructSelector - The callback we wish to use which means constructSelector in this instance
* @returns {string} The correct readable format
*/
var constructSubSelector = function (val, prefix, suffix, constructSelector) {
var content = prefix;
val.forEach(function (subItem) {
content += constructSelector(subItem);
});

return content + suffix;
};

// ==============================================================================
// Public Methods
// ==============================================================================

/**
* Constructs a syntax complete selector for our selector matching and warning output
*
* @param {object} val - The current node / part of our selector
* @returns {string} - Content: The current node with correct syntax e.g. class my-class = '.my-class'
*/
var constructSelector = function (val) {
var content = null;

if (val.is('id')) {
content = addGrammar(val, '#', '');
}

else if (val.is('class')) {
content = addGrammar(val, '.', '');
}

else if (simpleIdents.indexOf(val.type) !== -1) {
content = val.content;
}

else if (val.is('arguments')) {
content = constructSubSelector(val, '(', ')', constructSelector);
}

else if (val.is('attributeSelector')) {
content = constructSubSelector(val, '[', ']', constructSelector);
}

else if (val.is('atkeyword')) {
content = constructSubSelector(val, '@', '', constructSelector);
}

else if (val.is('placeholder')) {
content = constructSubSelector(val, '%', '', constructSelector);
}

else if (val.is('variable')) {
content = constructSubSelector(val, '$', '', constructSelector);
}

else if (val.is('pseudoClass')) {
content = constructSubSelector(val, ':', '', constructSelector);
}

else if (val.is('pseudoElement')) {
content = addGrammar(val, '::', '');
}

else if (val.is('nth')) {
content = addGrammar(val, '(', ')');
}

else if (val.is('nthSelector')) {
content = constructSubSelector(val, ':', '', constructSelector);
}

else if (val.is('parentheses')) {
content = constructSubSelector(val, '(', ')', constructSelector);
}

else if (val.is('space')) {
content = ' ';
}

else if (val.is('parentSelectorExtension') || val.is('attributeName') || val.is('attributeValue') || val.is('dimension')) {
content = constructSubSelector(val, '', '', constructSelector);
}

else if (val.is('interpolation')) {
content = constructSubSelector(val, '#{', '}', constructSelector);
}
return content;
};

module.exports = {
constructSelector: constructSelector
};
61 changes: 61 additions & 0 deletions tests/sass/selector-helpers/selector-helpers.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.test{
color: red;
}

#test{
color: red;
}

%test {
color: red;
}

.#{test} {
color: red
}

.test, #test {
color: red;
}

input[type="text"] {
color: red;
}

.test > li {
color: red;
}

span[lang~=en-us] {
color: red;
}

.block__element-one {
color: red;
}

@media (max-width: 200px) {
color: red;
}

##{$id} {
color: red;
}

.right-element::-ms-backdrop {
content: "right-prefixed-element";
}

.wrong-element:selection {
content: "wrong-element";
}

p:nth-of-type(2) {
margin: 0;
}

.test {
&__test {
color: red;
}
}
114 changes: 114 additions & 0 deletions tests/selector-helpers/selectorHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'use strict';

var assert = require('assert'),
selectorHelpers = require('../../lib/selector-helpers'),
groot = require('../../lib/groot'),
path = require('path'),
fs = require('fs'),
equal = require('deep-equal');

describe('selectorHelpers - constructSelector', function () {

var expectedSelectors = [
'.test',
'#test',
'%test',
'.#{test}',
6D40 '.test',
'#test',
'input[type="text"]',
'.test > li',
'span[lang~=en-us]',
'.block__element-one',
'##{$id}',
'.right-element::-ms-backdrop',
'.wrong-element:selection',
'p:nth-of-type(2)',
'.test',
'&__test'
],
selectorList = [];

before(function () {
var file = '../sass/selector-helpers/selector-helpers.scss',
ast = groot(fs.readFileSync(path.join(__dirname, file)), path.extname(file).replace('.', ''), file);

ast.traverseByType('selector', function (value) {
var ruleSet = '';
value.forEach(function (selectorContent) {
ruleSet += selectorHelpers.constructSelector(selectorContent);
});
selectorList.push(ruleSet);
});
});

//////////////////////////////
// contructSelector
//////////////////////////////

it('should return the correct class name', function (done) {
assert(equal(selectorList[0], expectedSelectors[0]));
done();
});

it('should return the correct ID name', function (done) {
assert(equal(selectorList[1], expectedSelectors[1]));
done();
});

it('should return the correct placeholder name', function (done) {
assert(equal(selectorList[2], expectedSelectors[2]));
done();
});

it('should return the correct interpolated selector name', function (done) {
assert(equal(selectorList[3], expectedSelectors[3]));
done();
});

it('should return the correct type selector name', function (done) {
assert(equal(selectorList[6], expectedSelectors[6]));
done();
});

it('should return the correct combinator selector name', function (done) {
assert(equal(selectorList[7], expectedSelectors[7]));
done();
});

it('should return the correct attribute selector name', function (done) {
assert(equal(selectorList[8], expectedSelectors[8]));
done();
});

it('should return the correct BEM selector name', function (done) {
assert(equal(selectorList[9], expectedSelectors[9]));
done();
});

it('should return the correct interpolated ID selector name', function (done) {
assert(equal(selectorList[10], expectedSelectors[10]));
done();
});

it('should return the correct pseudo element selector name', function (done) {
assert(equal(selectorList[11], expectedSelectors[11]));
done();
});

it('should return the correct pseudo selector name', function (done) {
assert(equal(selectorList[12], expectedSelectors[12]));
done();
});

it('should return the correct nth selector name', function (done) {
assert(equal(selectorList[13], expectedSelectors[13]));
done();
});

it('should return the correct parent selector name', function (done) {
assert(equal(selectorList[16], expectedSelectors[16]));
done();
});

});
0