diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..607da405 --- /dev/null +++ b/.npmignore @@ -0,0 +1,14 @@ +.github +coverage +docs +tests +.editorconfig +.eslintrc +.gitignore +.npmignore +.npmrc +.travis.yml +.nvmrc +appveyor.yml +CONTRIBUTING.md +CODE_OF_CONDUCT.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 140dac7a..e65a7b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Sass Lint Changelog +## v1.12.0 + +**October 3rd, 2017** + +**Fixes** +* Fixed an issue with custom properties being flagged in the misspelled-properties rule [#1122](https://github.com/sasstools/sass-lint/pull/1122) +* Fixed an issue where custom properties with colors in their name would be flagged as invalid by no-color-keywords [#1124](https://github.com/sasstools/sass-lint/pull/1124) +* Fixed a crash in empty-line-between-blocks where acessing the content of a parent node of type string would throw an error [#1125](https://github.com/sasstools/sass-lint/pull/1125) +* Functions and custom properties were being incorrectly flagged as invalid within rgba functions for no-color-literals [#1127](https://github.com/sasstools/sass-lint/pull/1127) +* Fixed an incorrect warning in space-after-colon when using `@at-root` [#1129](https://github.com/sasstools/sass-lint/pull/1129) +* Fixed an issue where interpolation was incorrectly flagging within the class-name-format rule [#1131](https://github.com/sasstools/sass-lint/pull/1131) + + +**New Features** +* Rollup.js integration added to integration list +* Added an npmignore to remove unnecessary dev files from a sass-lint install / release [#1132](https://github.com/sasstools/sass-lint/pull/1132) +* Added basic support for a `.sasslintrc` json config file [#1135](https://github.com/sasstools/sass-lint/pull/1135) +* Added two new options to the variable-for-property rule - [allow-map-get](https://github.com/sasstools/sass-lint/blob/master/docs/rules/variable-for-property.md#allow-map-get-true) & [allowed-functions](https://github.com/sasstools/sass-lint/blob/master/docs/rules/variable-for-property.md#allowed-functions-) - [#1128](https://github.com/sasstools/sass-lint/pull/1128) + ## v1.11.1 **August 28th, 2017** diff --git a/README.md b/README.md index 0d3cad35..20627d32 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ npm install sass-lint --save-dev ## Configuring -Sass-lint can be configured from a `.sass-lint.yml` file in your project. If you don't have one in the root of your project or you would like all your projects to follow a standard config file then you can specify the path to one in your project's `package.json` file. +Sass-lint can be configured from a `.sass-lint.yml` or `.sasslintrc` file in your project. The `.sasslintrc` file can be in either JSON format or YAML. Both formats are interchangeable easily using tools such as [json2yaml](https://www.json2yaml.com/). If you don't either file in the root of your project or you would like all your projects to follow a standard config file then you can specify the path to one in your project's `package.json` file with the `sasslintConfig` option. For example: ```javascript @@ -32,7 +32,7 @@ For example: } ``` -Use the [Sample Config](https://github.com/sasstools/sass-lint/tree/master/docs/sass-lint.yml) as a guide to create your own `.sass-lint.yml` config file. The default configuration can be found [here](https://github.com/sasstools/sass-lint/blob/master/lib/config/sass-lint.yml). +Use the [Sample Config (YAML)](https://github.com/sasstools/sass-lint/tree/master/docs/sass-lint.yml) or [Sample Config (JSON)](https://github.com/sasstools/sass-lint/tree/master/docs/sasslintrc) as a guide to create your own config file. The default configuration can be found [here](https://github.com/sasstools/sass-lint/blob/master/lib/config/sass-lint.yml). ### [Configuration Documentation](https://github.com/sasstools/sass-lint/tree/master/docs/options) @@ -48,7 +48,6 @@ The following are options that you can use to config the Sass Linter. * [merge-default-rules](https://github.com/sasstools/sass-lint/tree/master/docs/options/merge-default-rules.md) - Allows you to merge your rules with the default config file included with sass-lint * [output-file](https://github.com/sasstools/sass-lint/tree/master/docs/options/output-file.md) - Choose to write the linters output to a file - #### Files The `files` option contains two properties, `include` and `ignore`. Both can be set to either a [glob](https://github.com/isaacs/node-glob) or an array of glob strings/file paths depending on your projects' needs and setup. @@ -284,6 +283,10 @@ Our AST is [Gonzales-PE](https://github.com/tonyganch/gonzales-pe/tree/dev). Eac * [Gulp](https://www.npmjs.com/package/gulp-sass-lint) * [Grunt](https://github.com/sasstools/grunt-sass-lint) +## Module Bundler Integration + +* [rollup.js](https://github.com/kopacki/rollup-plugin-sass-lint) + ## IDE Integration * [Atom](https://atom.io/packages/linter-sass-lint) diff --git a/docs/rules/variable-for-property.md b/docs/rules/variable-for-property.md index 07a4e9f7..c2809da9 100644 --- a/docs/rules/variable-for-property.md +++ b/docs/rules/variable-for-property.md @@ -14,6 +14,10 @@ The `!important` flag will also be excluded when used. * `properties`: `[array of property names]` (defaults to empty array `[]`) +* `allow-map-get`: `true`/`false` (defaults to `true`) You may allow/disallow the use of `map-get()` as property values + +* `allowed-functions`: `[array of function names]` (defaults to empty array `[]`) + You may pass an array of properties you wish to enforce the use of variables for ```yaml @@ -26,10 +30,54 @@ variable-for-property: - 'content' ``` +You may pass an array of function names you wish to allow as property values + +```yaml + +variable-for-property: + - 1 + - + 'allowed-functions': + - 'my-map-func' + - 'palette' +``` + +*** full config example *** + +```yaml +variable-for-property: + - 1 + - + allow-map-get: true + allowed-functions: + - my-map-func + - palette + properties: + - margin + - content +``` + ## Examples By default `properties` is an empty array and therefore no properties are forced to use variables as values. +```scss +.bar { + content: ' '; + margin: 0; + + &__element { + margin: 0; + } +} + +@mixin red() { + margin: 0; +} +``` + +## [properties: []] + When `properties` contains the values shown in the options section example the following would be disallowed: ```scss @@ -65,6 +113,53 @@ When `properties` contains the values shown in the options section example the f ``` +## [allow-map-get: true] + +When allow-map-get is set to `true` and properties contains the `color` property, the following would be allowed + +```scss +.foo { + color: map-get(blueish, light); +} +``` + +When allow-map-get is set to `false` and properties contains the `color` property, the following would be disallowed + +```scss +.foo { + color: map-get(blueish, light); +} +``` + +## [allowed-functions: []] + +When `allowed-functions` contains the values shown in the options section and `properties` includes the `color` property the following would be disallowed: + +```scss +.foo { + color: disallowed-function($test, $vars); + + &__element { + color: invalid-func-name($test, $vars); + } +} +``` + +When `allowed-functions` contains the values shown in the options section and `properties` includes the `color` property the following would be disallowed: + +```scss +.foo { + color: my-map-func($allowed); + + &__element { + color: palette(blue, light); + } +} + +``` + +## Extra info + The `!important` flag will be excluded from any lint warnings. For example if `properties` contains the value `color` the following would be disallowed diff --git a/lib/config.js b/lib/config.js index 6f4f5590..3d79ccc1 100644 --- a/lib/config.js +++ b/lib/config.js @@ -52,6 +52,10 @@ module.exports = function (options, configPath) { } else { configPath = confHelpers.findFile(false, '.sass-lint.yml'); + + if (!configPath) { + configPath = confHelpers.findFile(false, '.sasslintrc'); + } } } else if (!pathIsAbsolute(configPath)) { diff --git a/lib/helpers.js b/lib/helpers.js index 0b9af3ce..9673d8af 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -309,7 +309,10 @@ helpers.attemptTraversal = function (node, traversalPath) { currentNodeList = [], processChildNode = function processChildNode (child) { child.forEach(traversalPath[i], function (n) { - nextNodeList.push(n); + if (n.content && typeof n.content !== 'string' && n.contains('interpolation')) { + return false; + } + return nextNodeList.push(n); }); }; diff --git a/lib/rules/empty-line-between-blocks.js b/lib/rules/empty-line-between-blocks.js index b4c12385..cc14f1f3 100644 --- a/lib/rules/empty-line-between-blocks.js +++ b/lib/rules/empty-line-between-blocks.js @@ -52,7 +52,7 @@ var findNearestReturnSCSS = function (parent, i) { var findNearestReturnSass = function (parent, i) { var previous; - if (parent.content[i - 1]) { + if (!parent.is('ident') && parent.content[i - 1]) { previous = parent.content[i - 1]; if (counter === 2) { diff --git a/lib/rules/no-color-keywords.js b/lib/rules/no-color-keywords.js index dcf21d1b..face6db9 100644 --- a/lib/rules/no-color-keywords.js +++ b/lib/rules/no-color-keywords.js @@ -15,11 +15,11 @@ var cssColors = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '../../data', * @returns {boolean} Whether the node is a valid type or not */ var checkValidParentType = function (node) { - if (node) { - return node.type === 'function' || node.type === 'variable'; + if (node && (node.is('function') || node.is('variable') || node.is('customProperty'))) { + return false; } - return false; + return true; }; module.exports = { @@ -30,7 +30,7 @@ module.exports = { ast.traverseByType('value', function (node) { node.traverse(function (elem, i, parent) { - if (elem.type === 'ident' && !checkValidParentType(parent)) { + if (elem.type === 'ident' && checkValidParentType(parent)) { var index = cssColors.indexOf(elem.content.toLowerCase()); if (index !== -1) { diff --git a/lib/rules/no-color-literals.js b/lib/rules/no-color-literals.js index 7d7159b4..0c6642dc 100644 --- a/lib/rules/no-color-literals.js +++ b/lib/rules/no-color-literals.js @@ -80,7 +80,7 @@ module.exports = { ast.traverseByTypes(['value', 'variable'], function (node, i, parent) { - // If we don't literals as variable names then check each variable name + // If we don't allow literals as variable names then check each variable name if (node.is('variable') && !parser.options['allow-variable-identifiers']) { if (cssColors.indexOf(node.content[0].content) !== -1) { result = helpers.addUnique(result, { @@ -127,15 +127,18 @@ module.exports = { // if rgba usage is allowed we need to make sure only variables are being passed to it. else if ( parser.options['allow-rgba'] && - funcType === 'rgba' && - valElem.content[1].content[0].type !== 'variable' && + funcType === 'rgba' && ( + valElem.content[1].content[0].type !== 'variable' && + valElem.content[1].content[0].type !== 'function' && + valElem.content[1].content[0].type !== 'customProperty' + ) && declarationType !== 'variable' ) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': valElem.start.line, 'column': valElem.start.column, - 'message': 'A color in variable form must be passed to rgba, literals are restricted', + 'message': 'Only variables or functions must be passed to rgba, literals are restricted', 'severity': parser.severity }); } diff --git a/lib/rules/no-misspelled-properties.js b/lib/rules/no-misspelled-properties.js index 7db9d105..02b1a26b 100644 --- a/lib/rules/no-misspelled-properties.js +++ b/lib/rules/no-misspelled-properties.js @@ -43,7 +43,7 @@ var buildPartialProperty = function (valBlock, currentProperty) { var value = node.first('value'); skipProps++; - if (prop.first().is('ident')) { + if (prop && prop.first().is('ident')) { if (value.contains('block')) { propList = propList.concat( buildPartialProperty(value.first('block'), @@ -77,7 +77,10 @@ module.exports = { ast.traverseByType('declaration', function (node) { var prop = node.first('property'); - var containsInterp = prop.contains('interpolation'); + // declaration may include custom properties etc, check that prop is defined here + if (!prop) { + return false; + } // If we've already checked declarations in a multiline we can skip those decs here if (skipProps) { skipProps -= 1; @@ -123,7 +126,7 @@ module.exports = { * 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 (prop.contains('interpolation')) { if (!helpers.isPartialStringMatch(curProperty, propertyList)) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, diff --git a/lib/rules/space-after-colon.js b/lib/rules/space-after-colon.js index b5b4f9a8..20d39dc5 100644 --- a/lib/rules/space-after-colon.js +++ b/lib/rules/space-after-colon.js @@ -11,29 +11,30 @@ module.exports = { var result = []; ast.traverseByTypes(['propertyDelimiter', 'operator'], function (delimiter, i, parent) { - if (delimiter.content === ':') { - var next = parent.content[i + 1]; - - if (next && next.is('space')) { - if (!parser.options.include) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': next.start.line, - 'column': next.start.column, - 'message': 'No space allowed after `:`', - 'severity': parser.severity - }); + if (!parent.is('atrule')) { + if (delimiter.content === ':') { + var next = parent.content[i + 1]; + if (next && next.is('space')) { + if (!parser.options.include) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': next.start.line, + 'column': next.start.column, + 'message': 'No space allowed after `:`', + 'severity': parser.severity + }); + } } - } - else { - if (parser.options.include) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': delimiter.start.line, - 'column': delimiter.start.column, - 'message': 'Space expected after `:`', - 'severity': parser.severity - }); + else { + if (parser.options.include) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': delimiter.start.line, + 'column': delimiter.start.column, + 'message': 'Space expected after `:`', + 'severity': parser.severity + }); + } } } } diff --git a/lib/rules/variable-for-property.js b/lib/rules/variable-for-property.js index b006595b..dd73361c 100644 --- a/lib/rules/variable-for-property.js +++ b/lib/rules/variable-for-property.js @@ -14,10 +14,10 @@ var whitelistedValues = ['inherit', 'initial', 'transparent', 'none', 'currentCo */ var isValidProperty = function (propertyElem) { if (propertyElem) { - if (propertyElem.type === 'variable') { + if (propertyElem.is('variable')) { return true; } - else if (propertyElem.type === 'ident' && whitelistedValues.indexOf(propertyElem.content) !== -1) { + else if (propertyElem.is('ident') && whitelistedValues.indexOf(propertyElem.content) !== -1) { return true; } } @@ -37,10 +37,41 @@ var isIgnoredType = function (propertyElem) { return false; }; +/** + * Checks If the property type is a function and whether it is allowed + * + * @param {Object} propertyElem - The property element + * @param {boolean} allowMap - Whether the user has specified to allow Sass function map-get + * @param {Array} functionWhitelist - An array of string - function names we wish to allow + * @returns {boolean} Whether the property is an ignored type or not + */ +var isIgnoredFunction = function (propertyElem, allowMap, functionWhitelist) { + if (propertyElem && propertyElem.is('function')) { + var funcIdent = propertyElem.first('ident'); + + // allow custom properties as values + if (funcIdent.content === 'var') { + return true; + } + + if (allowMap && funcIdent.content === 'map-get') { + return true; + } + + if (functionWhitelist.length) { + return functionWhitelist.indexOf(funcIdent.content) !== -1; + } + } + return false; +}; + + module.exports = { 'name': 'variable-for-property', 'defaults': { - 'properties': [] + 'properties': [], + 'allow-map-get': true, + 'allowed-functions': [] }, 'detect': function (ast, parser) { var result = []; @@ -54,7 +85,22 @@ module.exports = { if (declarationType === 'ident') { if (parser.options.properties.indexOf(declarationIdent) !== -1) { node.forEach(function (valElem) { - if (!isValidProperty(valElem) && !isIgnoredType(valElem)) { + + if (valElem.is('function') && !isIgnoredFunction(valElem, parser.options['allow-map-get'], parser.options['allowed-functions'])) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': declaration.start.line, + 'column': declaration.start.column, + 'message': 'The function passed to \'' + declarationIdent + '\' is not allowed', + 'severity': parser.severity + }); + } + else if ( + !valElem.is('function') && + !isValidProperty(valElem) && + !isIgnoredType(valElem) && + !valElem.is('interpolation') + ) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': declaration.start.line, diff --git a/package.json b/package.json index b184547a..7bb28088 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sass-lint", - "version": "1.11.1", + "version": "1.12.0", "description": "All Node Sass linter!", "main": "index.js", "scripts": { diff --git a/tests/cli.js b/tests/cli.js index df7c7fef..b2f7bb0c 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -239,6 +239,47 @@ describe('cli', function () { }); }); + // Test default config files + + it('should return JSON from .sass-lint.yml', function (done) { + var command = 'node ../../../bin/sass-lint ../../cli/cli.scss --verbose'; + + exec(command, { cwd: path.join(__dirname, 'yml', '.sass-lint.yml') }, function (err, stdout) { + + if (err) { + return done(err); + } + else { + try { + JSON.parse(stdout); + return done(); + } + catch (e) { + return done(new Error('Not JSON')); + } + } + }); + }); + + it('should return JSON from .sasslintrc', function (done) { + var command = 'node ../../../bin/sass-lint ../../cli/cli.scss -c ".sasslintrc" --verbose'; + + exec(command, { cwd: path.join(__dirname, 'yml', '.sasslintrc') }, function (err, stdout) { + if (err) { + return done(err); + } + else { + try { + JSON.parse(stdout); + return done(); + } + catch (e) { + return done(new Error('Not JSON')); + } + } + }); + }); + // Test custom config path it('should return JSON from a custom config', function (done) { diff --git a/tests/rules/class-name-format.js b/tests/rules/class-name-format.js index c5f5365c..751b05a2 100644 --- a/tests/rules/class-name-format.js +++ b/tests/rules/class-name-format.js @@ -12,7 +12,7 @@ describe('class name format - scss', function () { lint.test(file, { 'class-name-format': 1 }, function (data) { - lint.assert.equal(31, data.warningCount); + lint.assert.equal(33, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(30, data.warningCount); + lint.assert.equal(32, data.warningCount); done(); }); }); @@ -40,7 +40,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(40, data.warningCount); + lint.assert.equal(42, data.warningCount); done(); }); }); @@ -54,7 +54,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(40, data.warningCount); + lint.assert.equal(43, data.warningCount); done(); }); }); @@ -68,7 +68,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(36, data.warningCount); + lint.assert.equal(37, data.warningCount); done(); }); }); @@ -82,7 +82,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(17, data.warningCount); + lint.assert.equal(18, data.warningCount); done(); }); }); @@ -110,7 +110,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(42, data.warningCount); + lint.assert.equal(45, data.warningCount); done(); }); }); @@ -126,7 +126,7 @@ describe('class name format - scss', function () { } ] }, function (data) { - lint.assert.equal(38, data.warningCount); + lint.assert.equal(41, data.warningCount); lint.assert.equal(data.messages[0].message, message); done(); }); @@ -143,7 +143,7 @@ describe('class name format - sass', function () { lint.test(file, { 'class-name-format': 1 }, function (data) { - lint.assert.equal(31, data.warningCount); + lint.assert.equal(33, data.warningCount); done(); }); }); @@ -157,7 +157,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(30, data.warningCount); + lint.assert.equal(32, data.warningCount); done(); }); }); @@ -171,7 +171,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(40, data.warningCount); + lint.assert.equal(42, data.warningCount); done(); }); }); @@ -185,7 +185,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(40, data.warningCount); + lint.assert.equal(43, data.warningCount); done(); }); }); @@ -199,7 +199,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(36, data.warningCount); + lint.assert.equal(37, data.warningCount); done(); }); }); @@ -213,7 +213,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(17, data.warningCount); + lint.assert.equal(18, data.warningCount); done(); }); }); @@ -241,7 +241,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(42, data.warningCount); + lint.assert.equal(45, data.warningCount); done(); }); }); @@ -257,7 +257,7 @@ describe('class name format - sass', function () { } ] }, function (data) { - lint.assert.equal(38, data.warningCount); + lint.assert.equal(41, data.warningCount); lint.assert.equal(data.messages[0].message, message); done(); }); diff --git a/tests/rules/no-color-literals.js b/tests/rules/no-color-literals.js index 00a1400d..11c320d5 100644 --- a/tests/rules/no-color-literals.js +++ b/tests/rules/no-color-literals.js @@ -12,7 +12,7 @@ describe('no color literals - scss', function () { lint.test(file, { 'no-color-literals': 1 }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(19, data.warningCount); done(); }); }); @@ -40,7 +40,7 @@ describe('no color literals - scss', function () { } ] }, function (data) { - lint.assert.equal(20, data.warningCount); + lint.assert.equal(23, data.warningCount); done(); }); }); @@ -54,7 +54,7 @@ describe('no color literals - scss', function () { } ] }, function (data) { - lint.assert.equal(19, data.warningCount); + lint.assert.equal(22, data.warningCount); done(); }); }); @@ -86,7 +86,7 @@ describe('no color literals - sass', function () { lint.test(file, { 'no-color-literals': 1 }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(19, data.warningCount); done(); }); }); @@ -114,7 +114,7 @@ describe('no color literals - sass', function () { } ] }, function (data) { - lint.assert.equal(20, data.warningCount); + lint.assert.equal(23, data.warningCount); done(); }); }); @@ -128,7 +128,7 @@ describe('no color literals - sass', function () { } ] }, function (data) { - lint.assert.equal(19, data.warningCount); + lint.assert.equal(22, data.warningCount); done(); }); }); diff --git a/tests/rules/space-after-colon.js b/tests/rules/space-after-colon.js index 734538b5..50576ad5 100644 --- a/tests/rules/space-after-colon.js +++ b/tests/rules/space-after-colon.js @@ -12,7 +12,7 @@ describe('space after colon - scss', function () { lint.test(file, { 'space-after-colon': 1 }, function (data) { - lint.assert.equal(3, data.warningCount); + lint.assert.equal(4, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space after colon - scss', function () { } ] }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); diff --git a/tests/rules/variable-for-property.js b/tests/rules/variable-for-property.js index 8e803347..8b010599 100644 --- a/tests/rules/variable-for-property.js +++ b/tests/rules/variable-for-property.js @@ -45,6 +45,59 @@ describe('variable for property - scss', function () { ] } ] + }, function (data) { + lint.assert.equal(2, data.warningCount); + done(); + }); + }); + + it('[properties: [\'color\'], allow-map-get: true]', function (done) { + lint.test(file, { + 'variable-for-property': [ + 1, + { + 'properties': [ + 'color' + ], + 'allow-map-get': true + } + ] + }, function (data) { + lint.assert.equal(2, data.warningCount); + done(); + }); + }); + + it('[properties: [\'color\'], allow-map-get: false]', function (done) { + lint.test(file, { + 'variable-for-property': [ + 1, + { + 'properties': [ + 'color' + ], + 'allow-map-get': false + } + ] + }, function (data) { + lint.assert.equal(3, data.warningCount); + done(); + }); + }); + + it('[properties: [\'color\'], allowed-functions: [\'my-map-func\']]', function (done) { + lint.test(file, { + 'variable-for-property': [ + 1, + { + 'properties': [ + 'color' + ], + 'allowed-functions': [ + 'my-map-func' + ] + } + ] }, function (data) { lint.assert.equal(1, data.warningCount); done(); @@ -95,6 +148,59 @@ describe('variable for property - sass', function () { ] } ] + }, function (data) { + lint.assert.equal(2, data.warningCount); + done(); + }); + }); + + it('[properties: [\'color\'], allow-map-get: true]', function (done) { + lint.test(file, { + 'variable-for-property': [ + 1, + { + 'properties': [ + 'color' + ], + 'allow-map-get': true + } + ] + }, function (data) { + lint.assert.equal(2, data.warningCount); + done(); + }); + }); + + it('[properties: [\'color\'], allow-map-get: false]', function (done) { + lint.test(file, { + 'variable-for-property': [ + 1, + { + 'properties': [ + 'color' + ], + 'allow-map-get': false + } + ] + }, function (data) { + lint.assert.equal(3, data.warningCount); + done(); + }); + }); + + it('[properties: [\'color\'], allowed-functions: [\'my-map-func\']]', function (done) { + lint.test(file, { + 'variable-for-property': [ + 1, + { + 'properties': [ + 'color' + ], + 'allowed-functions': [ + 'my-map-func' + ] + } + ] }, function (data) { lint.assert.equal(1, data.warningCount); done(); diff --git a/tests/sass/class-name-format.sass b/tests/sass/class-name-format.sass index f63b1fe9..0b22788e 100644 --- a/tests/sass/class-name-format.sass +++ b/tests/sass/class-name-format.sass @@ -94,3 +94,20 @@ .strict-bem__elem_key--fail color: red + + +// Issue #988 - Fix interpolation in class names + +.#{$primary-class}--small + color: red + + &__child + color: red + +.test + &#{$primary-class} + color: red + + &__block + &--mod + color: blue diff --git a/tests/sass/class-name-format.scss b/tests/sass/class-name-format.scss index bbb2452e..cffedbc0 100644 --- a/tests/sass/class-name-format.scss +++ b/tests/sass/class-name-format.scss @@ -133,3 +133,24 @@ .strict-bem__elem_key--fail { color: red; } + +// Issue #988 - Fix interpolation in class names + +.#{$primary-class}--small { + color: red; + &__child { + color: red; + } +} + +.test { + &#{$primary-class} { + color: red; + } + + &__block { + &--mod { + color: blue; + } + } +} diff --git a/tests/sass/no-color-keywords.sass b/tests/sass/no-color-keywords.sass index a619da9f..127727b1 100644 --- a/tests/sass/no-color-keywords.sass +++ b/tests/sass/no-color-keywords.sass @@ -31,3 +31,5 @@ $other: rgba($blue, 0.3) // Issue #717 - rule trips over Sass color function names $colors: ( 'red': red($color), 'green': green($color), 'blue': blue($color) ) + +$badge-bg: var(--red) diff --git a/tests/sass/no-color-keywords.scss b/tests/sass/no-color-keywords.scss index 3bcea3d7..4aac84b4 100644 --- a/tests/sass/no-color-keywords.scss +++ b/tests/sass/no-color-keywords.scss @@ -42,3 +42,5 @@ $colors: ( 'green': green($color), 'blue': blue($color) ); + +$badge-bg: var(--red); diff --git a/tests/sass/no-color-literals.sass b/tests/sass/no-color-literals.sass index f7a88d70..56801878 100644 --- a/tests/sass/no-color-literals.sass +++ b/tests/sass/no-color-literals.sass @@ -113,3 +113,11 @@ $hsla: hsla(40, 50%, 50%, .3) .rgba-enabled color: rgba($hexVar, .3) + +.some-element + border-bottom: 1px solid rgba($light-gray, 0.5) + color: rgba(shade($some-color), 0.5) + +.color + border: 1px solid var(--white) + color: rgba(var(--my-color), 0.5) diff --git a/tests/sass/no-color-literals.scss b/tests/sass/no-color-literals.scss index 1e62b699..4e32f753 100644 --- a/tests/sass/no-color-literals.scss +++ b/tests/sass/no-color-literals.scss @@ -122,3 +122,13 @@ $black: #000; .rgba-enabled { color: rgba($hexVar, .3); } + +.some-element { + border-bottom: 1px solid rgba($light-gray, .5); + color: rgba(shade($some-color), 0.5); // functions should be allowed as rgba params +} + +.color { + border: 1px solid var(--white); + color: rgba(var(--my-color), 0.5); +} diff --git a/tests/sass/no-misspelled-properties.sass b/tests/sass/no-misspelled-properties.sass index 645c5e7e..773a2b87 100644 --- a/tests/sass/no-misspelled-properties.sass +++ b/tests/sass/no-misspelled-properties.sass @@ -77,3 +77,6 @@ $red: #ff0000 color: red background-colr: blue font-size: 12rem + +.element + --main-bg-color: brown diff --git a/tests/sass/no-misspelled-properties.scss b/tests/sass/no-misspelled-properties.scss index 7e9afcb1..7fc2003c 100644 --- a/tests/sass/no-misspelled-properties.scss +++ b/tests/sass/no-misspelled-properties.scss @@ -104,3 +104,7 @@ $red: #ff0000; background-colr: blue; // 14 font-size: 12rem; } + +.element { + --main-bg-color: brown; +} diff --git a/tests/sass/space-after-colon.sass b/tests/sass/space-after-colon.sass index 302c35a7..f2485e8f 100644 --- a/tests/sass/space-after-colon.sass +++ b/tests/sass/space-after-colon.sass @@ -14,3 +14,9 @@ $qux: 'qux' $norf:'norf' + +.foo + @at-root button#{&}:hover + +.foo:hover + +test diff --git a/tests/sass/space-after-colon.scss b/tests/sass/space-after-colon.scss index 967c5949..76f8db13 100644 --- a/tests/sass/space-after-colon.scss +++ b/tests/sass/space-after-colon.scss @@ -14,3 +14,17 @@ $qux: 'qux'; $norf:'norf'; + +.foo { + @at-root button#{&}:hover { + } +} + +.foo:hover { + @include test; +} + +$map: { + test:'red', + other: blue, +} diff --git a/tests/sass/variable-for-property.sass b/tests/sass/variable-for-property.sass index 78047682..5f34c217 100644 --- a/tests/sass/variable-for-property.sass +++ b/tests/sass/variable-for-property.sass @@ -35,3 +35,18 @@ .issue color: red !important + +.test + color: map-get($blue) + +.interp-test + color: #{var} + + // ensure interp is not flagged + +.func-name-test + color: my-map-func(blue, light) + +.custom-prop + // ensure custom properties are valid + color: var(--my-prop) diff --git a/tests/sass/variable-for-property.scss b/tests/sass/variable-for-property.scss index b9621772..cba29b09 100644 --- a/tests/sass/variable-for-property.scss +++ b/tests/sass/variable-for-property.scss @@ -39,3 +39,19 @@ .t-neutral { color: red !important; } + +.test { + color: map-get($blue); +} + +.interp-test { + color: #{var}; // ensure interp is not flagged +} + +.func-name-test { + color: my-map-func(blue, light); +} + +.custom-prop { + color: var(--my-prop); // ensure custom properties are valid +} diff --git a/tests/yml/.sass-lint.yml/.sass-lint.yml b/tests/yml/.sass-lint.yml/.sass-lint.yml new file mode 100644 index 00000000..2ba37375 --- /dev/null +++ b/tests/yml/.sass-lint.yml/.sass-lint.yml @@ -0,0 +1,8 @@ +options: + formatter: json + cache-config: false + merge-default-rules: false +files: + include: '**/*.s+(a|c)ss' +rules: + no-color-keywords: 1 diff --git a/tests/yml/.sasslintrc/.sasslintrc b/tests/yml/.sasslintrc/.sasslintrc new file mode 100644 index 00000000..a7505d0c --- /dev/null +++ b/tests/yml/.sasslintrc/.sasslintrc @@ -0,0 +1,13 @@ +{ + "options": { + "formatter": "json", + "cache-config": false, + "merge-default-rules": false + }, + "files": { + "include": "**/*.s+(a|c)ss" + }, + "rules": { + "no-color-keywords": 1 + } +}