UNPKG

canonical

Version:

Canonical code style linter and formatter for JavaScript, SCSS, CSS and JSON.

92 lines (76 loc) 3.1 kB
'use strict'; var helpers = require('../helpers'), capitalize = require('lodash.capitalize'), kebabcase = require('lodash.kebabcase'); // Our nestable selector types, separated by type for ease of use with rules // we replace ident with 'selector' for readability' var nestableElements = ['selector', 'class', 'id'], nestableAttributes = ['attribute'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; /** * Formats a string from camelCase to hyphens and capitalizes * @param {string} str - The string to be formatted * @returns {string} A hyphenated and capitalized string */ var formatOutput = function (str) { return capitalize(kebabcase(str)); }; module.exports = { 'name': 'force-element-nesting', 'defaults': {}, 'detect': function (ast, parser) { var result = [], elements = nestableElements.concat(nestableAttributes, nestablePseudo); ast.traverseByType('ruleset', function (ruleset) { var delimOrder = []; ruleset.forEach('selector', function (selector) { // Where we'll store the previous value var previousVal; // Keep track of the order of elements & delimeters selector.forEach(function (el) { var curNode = helpers.mapDelims(el); if (curNode !== false) { delimOrder.push(curNode); } }); selector.forEach('simpleSelector', function (simpleSelector) { // check if the next selector is proceeded by a delimiter // if it is add it to the curSelector output var nextType = delimOrder[0]; if (nextType === 'd') { // Empty the previous value store previousVal = null; // remove the next delim and selector from our order as we are only // looping over selectors delimOrder.splice(0, 2); } else { // if no delim then just remove the current selectors marker in the // order array delimOrder.splice(0, 1); } // Loop each selector while checking if it should be nested simpleSelector.forEach(function (node) { // Construct the selector and store the current selector value var constructedSelector = helpers.constructSelector(node), currentVal = constructedSelector.type; if (helpers.isNestable(currentVal, previousVal, elements, nestableElements)) { helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': node.start.line, 'column': node.start.column, 'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal, 'severity': parser.severity }); } if (currentVal) { // Store current value as previous and continue with the loop previousVal = currentVal; } }); }); }); }); return result; } };