Skip to content

Commit

Permalink
Merge pull request #1106 from sasstools/feature/update-misspelled-pro…
Browse files Browse the repository at this point in the history
…perties

Handle multiline properties & update css list
  • Loading branch information
DanPurdy authored Aug 26, 2017
2 parents ebbc426 + d157eb8 commit aefde6e
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 26 deletions.
136 changes: 118 additions & 18 deletions lib/rules/no-misspelled-properties.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
'use strict';

var helpers = require('../helpers'),
yaml = require('js-yaml'),
fs = require('fs'),
path = require('path');

var properties = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '../../data', 'properties.yml'), 'utf8')).split(' ');
var helpers = require('../helpers');
var properties = require('known-css-properties').all;

/**
* Combine the valid property array and the array of extras into a new array
Expand All @@ -18,28 +14,133 @@ var getCombinedList = function (props, extras) {
return props.concat(extras);
};

/**
* Combines the base property name with the current property name to correct a full property name
*
* @param {String} baseName - The base property name
* @param {String} name - The property name to append to our base property name
* @returns {String} The constructed property name
*/
var generateName = function (baseName, name) {
return baseName + '-' + name;
};

/**
* Recursive function to build up an array of property names when encountering multiline properties
*
* @param {Object} valBlock - The current block node from within our value
* @param {Object} currentProperty - The current base property name i.e. border-
* @param {Number} propsCounted - The number of properties encountered in our multiline so far
* @returns {Array} Array of objects containing our property and line/col info etc
*/
var buildPartialProperty = function (valBlock, currentProperty, propsCounted) {
var propList = [];
var propsEncountered = propsCounted;
if (valBlock.contains('declaration')) {
valBlock.forEach('declaration', function (node) {
var prop = node.first('property');
var value = node.first('value');
propsEncountered++;

if (prop.first().is('ident')) {
if (value.contains('block')) {
propList = propList.concat(
buildPartialProperty(value.first('block'),
{
name: generateName(currentProperty.name, prop.first('ident').content)
},
propsEncountered
)
);
}
else {
propList.push({
name: generateName(currentProperty.name, prop.first('ident').content),
line: prop.first().start.line,
col: prop.first().start.column,
propsEncountered: propsEncountered
});
}
}
});
}
return propList;
};

module.exports = {
'name': 'no-misspelled-properties',
'defaults': {
'extra-properties': []
},
'detect': function (ast, parser) {
var result = [];
var toSkip = 0;
var propertyList = getCombinedList(properties, parser.options['extra-properties']);

ast.traverseByType('property', function (node) {
if (node.first().is('ident')) {
var curProperty = node.first().content,
propertyList = getCombinedList(properties, parser.options['extra-properties']);
ast.traverseByType('declaration', function (node) {
var prop = node.first('property');
var containsInterp = prop.contains('interpolation');
// If we've already checked declarations in a multiline we can skip those decs here
if (toSkip) {
toSkip--;
return !toSkip;
}
// make sure our first node within our property is an ident
if (!prop.first() || !prop.first().is('ident')) {
return false;
}

if (curProperty.charAt(0) === '-') {
curProperty = helpers.stripPrefix(curProperty);
}
var curProperty = prop.first() && prop.first().is('ident') && prop.first('ident').content;
var value = node.first('value');
var fullProperties = [];

if (helpers.isPartialStringMatch(curProperty, propertyList)) {
return false;
if (value.contains('block')) {
// encountered a multiline property, we should build the property list here
fullProperties = buildPartialProperty(
value.first('block'),
{
name: curProperty,
line: prop.first('ident').start.line,
col: prop.first('ident').start.column
},
0
);
}
// If we have multiline properties
if (fullProperties.length) {
fullProperties.forEach(function (constrProp) {
// Add the number of property declarations we've already checked here so we can skip them
toSkip += constrProp.propsEncountered;
// Check if the property exists in our list
if (propertyList.indexOf(constrProp.name) === -1) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': constrProp.line,
'column': constrProp.col,
'message': 'Property `' + constrProp.name + '` appears to be spelled incorrectly',
'severity': parser.severity
});
}
});
}
else if (curProperty && curProperty.length) {
/*
* If our property name contains interpolation we need to make a best guess by using a
* partial string match as we can't be 100% on the context
*/
if (containsInterp) {
if (!helpers.isPartialStringMatch(curProperty, propertyList)) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': node.start.line,
'column': node.start.column,
'message': 'Property `' + curProperty + '` appears to be spelled incorrectly',
'severity': parser.severity
});
}
}

if (curProperty.length > 0) {
// Otherwise it's just a normal string property name, lets check it here
else if (propertyList.indexOf(curProperty) === -1) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': node.start.line,
Expand All @@ -49,7 +150,6 @@ module.exports = {
});
}
}

return false;
});

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"globule": "^1.0.0",
"gonzales-pe": "^4.1.1",
"js-yaml": "^3.5.4",
"known-css-properties": "^0.3.0",
"lodash.capitalize": "^4.1.0",
"lodash.kebabcase": "^4.0.0",
"merge": "^1.2.0",
Expand Down
12 changes: 6 additions & 6 deletions tests/rules/no-misspelled-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('no misspelled properties - scss', function () {
lint.test(file, {
'no-misspelled-properties': 1
}, function (data) {
lint.assert.equal(7, data.warningCount);
lint.assert.equal(9, data.warningCount);
done();
});
});
Expand All @@ -28,7 +28,7 @@ describe('no misspelled properties - scss', function () {
}
]
}, function (data) {
lint.assert.equal(5, data.warningCount);
lint.assert.equal(8, data.warningCount);
done();
});
});
Expand All @@ -45,7 +45,7 @@ describe('no misspelled properties - scss', function () {
}
]
}, function (data) {
lint.assert.equal(4, data.warningCount);
lint.assert.equal(7, data.warningCount);
done();
});
});
Expand All @@ -61,7 +61,7 @@ describe('no misspelled properties - sass', function () {
lint.test(file, {
'no-misspelled-properties': 1
}, function (data) {
lint.assert.equal(7, data.warningCount);
lint.assert.equal(9, data.warningCount);
done();
});
});
Expand All @@ -77,7 +77,7 @@ describe('no misspelled properties - sass', function () {
}
]
}, function (data) {
lint.assert.equal(5, data.warningCount);
lint.assert.equal(8, data.warningCount);
done();
});
});
Expand All @@ -94,7 +94,7 @@ describe('no misspelled properties - sass', function () {
}
]
}, function (data) {
lint.assert.equal(4, data.warningCount);
lint.assert.equal(7, data.warningCount);
done();
});
});
Expand Down
2 changes: 0 additions & 2 deletions tests/sass/no-misspelled-properties.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ $red: #ff0000;
background-sizer: contain;
colors: $red;
transit1on: width 2s;

}

.test-correct {
Expand All @@ -15,7 +14,6 @@ $red: #ff0000;
background-size: contain;
color: $red;
transition: width 2s;

}

.test-interp {
Expand Down

0 comments on commit aefde6e

Please sign in to comment.