canonical
Version:
Canonical code style linter and formatter for JavaScript, SCSS, CSS and JSON.
206 lines (170 loc) • 5.54 kB
JavaScript
'use strict';
var helpers = require('../helpers');
var counter,
syntax;
var findNearestReturnSCSS = function (parent, i) {
var previous,
doublePrevious,
space;
if (parent.content[i - 1]) {
previous = parent.content[i - 1];
if (i >= 2) {
doublePrevious = parent.content[i - 2];
// First check to see that the previous line is not a new line as if it is
// we don't want to recursively run the function again
if (!helpers.isEmptyLine(previous.content)) {
if (doublePrevious.type.indexOf('Comment') !== -1) {
return findNearestReturnSCSS(parent, i - 1);
}
}
}
if (i >= 1) {
if (previous.type.indexOf('Comment') !== -1) {
return findNearestReturnSCSS(parent, i - 1);
}
if (previous.type === 'space') {
space = helpers.isEmptyLine(previous.content);
// If there's not a new line and it's the first within the block, ignore
if (!space && (i - 1 === 0)) {
return false;
}
return {
'space': space,
'previous': previous
};
}
}
}
};
var findNearestReturnSass = function (parent, i) {
var previous;
if (parent.content[i - 1]) {
previous = parent.content[i - 1];
if (counter === 2) {
return {
space: true,
previous: previous
};
}
if (previous.is('space') || previous.is('declarationDelimiter')) {
if (helpers.hasEOL(previous.content)) {
counter++;
}
return findNearestReturnSass(parent, i - 1);
}
// If ruleset, we must reset the parent to be the previous node and
// loop through that
else if (previous.is('ruleset') || previous.is('include')) {
var previousNode = previous.content[previous.content.length - 1];
// Set the i parameter for findNearestReturn to be the length of the
// content array in order to get the last one
return findNearestReturnSass(previousNode, previousNode.content.length);
}
else {
counter = 0;
if (previous.type.indexOf('Comment') !== -1) {
// If it's the first line
if (previous.start.line === 1) {
return {
space: true,
previous: previous
};
}
return findNearestReturnSass(parent, i - 1);
}
}
}
return {
space: false,
previous: previous
};
};
module.exports = {
'name': 'empty-line-between-blocks',
'defaults': {
'include': true,
'allow-single-line-rulesets': true
},
'detect': function (ast, parser) {
var result = [];
syntax = ast.syntax;
ast.traverseByType('ruleset', function (node, j, p) {
var space;
if ((node.start.line === node.end.line) && parser.options['allow-single-line-rulesets']) {
return false;
}
if (syntax === 'scss') {
space = findNearestReturnSCSS(p, j);
if (space) {
if (parser.options.include && !space.space && j !== 1) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': space.previous.end.line,
'column': 1,
'message': 'Space expected between blocks',
'severity': parser.severity
});
}
else if (!parser.options.include && space.space) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': space.previous.end.line,
'column': 1,
'message': 'Space not allowed between blocks',
'severity': parser.severity
});
}
}
}
else if (syntax === 'sass') {
// Reset the counter for each ruleset
counter = 0;
if (node.is('ruleset')) {
node.forEach('block', function (block, i, parent) {
var previous;
// Capture the previous node
if (parent.content[i - 1]) {
previous = parent.content[i - 1];
}
else {
// Else set the block to act as the previous node
previous = block;
}
// If it's a new line, lets go back up to the selector
if (previous.is('space') && helpers.hasEOL(previous.content)) {
// If we have a node (most likely type of selector)
if (parent.content[i - 2]) {
if (typeof parent.content[i - 3] === 'undefined') {
space = findNearestReturnSass(p, j);
}
}
}
});
}
if (space && space.previous) {
if (space.previous.start.line !== 1) {
if (parser.options.include && !space.space) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': space.previous.end.line + 1,
'column': 1,
'message': 'Space expected between blocks',
'severity': parser.severity
});
}
else if (!parser.options.include && space.space) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': space.previous.end.line + 1,
'column': 1,
'message': 'Space not allowed between blocks',
'severity': parser.severity
});
}
}
}
}
});
return result;
}
};