Socket
Socket
Sign inDemoInstall

stylelint

Package Overview
Dependencies
Maintainers
3
Versions
238
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

stylelint - npm Package Compare versions

Comparing version 5.4.0 to 6.0.0

dist/rules/at-rule-name-case/__test__/index.js

19

CHANGELOG.md

@@ -0,1 +1,20 @@

# 6.0.0
- Changed: `CssSyntaxError` is no longer thrown but reported alongside warnings.
- Added: new look for standard formatter and support for arbitrary severity names.
- Added: `indentInsideParens` option to `indentation`.
- Added: `indentClosingBrace` option to `indentation`.
- Added: `consecutive-duplicates` option for `declaration-block-no-duplicate-properties` rule.
- Added: `unit-case` rule.
- Added: exposed `stylelint.utils.cssWordIsVariable()`.
- Added: `property-case` rule.
- Added: `function-name-case` rule.
- Added: `selector-pseudo-element-case` rule.
- Added: `value-keyword-case` rule.
- Added: `selector-pseudo-class-case` rule.
- Added: `at-rule-name-case` rule.
- Fixed: `block-no-empty` no longer delivers false positives for less syntax.
- Fixed: `declaration-block-trailing-semicolon` better understands nested at-rules.
- Fixed: `number-zero-length-no-unit` now work with `q` unit and ignores `s`, `ms`, `kHz`, `Hz`, `dpcm`, `dppx`, `dpi` units
# 5.4.0

@@ -2,0 +21,0 @@

14

CONTRIBUTING.md
# Contributing
Any help is welcome and appreciated.
## Ways to contribute
- Adding new rules. Whether it's one of the [few](https://github.com/stylelint/stylelint/labels/type%3A%20rule) that we've still to do, or something that you need and we've not thought of yet.
- Expanding the [documentation](https://github.com/stylelint/stylelint/tree/master/docs).
- Working on other [open issues](https://github.com/stylelint/stylelint/issues).
## Communication
We mostly communicate via [issues](https://github.com/stylelint/stylelint/issues) and [pull requests](https://github.com/stylelint/stylelint/pulls). There is also [stackoverflow](http://stackoverflow.com/questions/tagged/stylelint), which is our preferred QA forum. Tag your post with "stylelint".
### Conduct
Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer, merely an optimal answer given a set of values and circumstances.

@@ -18,0 +4,0 @@

116

dist/formatters/stringFormatter.js

@@ -12,6 +12,3 @@ "use strict";

return results.reduce(function (output, result) {
output += minimalFormatter({
messages: result.warnings,
source: result.source
});
output += formatter(result.warnings, result.source);
return output;

@@ -25,2 +22,6 @@ }, output);

var _path = require("path");
var _path2 = _interopRequireDefault(_path);
var _lodash = require("lodash");

@@ -30,13 +31,24 @@

var _formatter = require("postcss-reporter/lib/formatter");
var _logSymbols = require("log-symbols");
var _formatter2 = _interopRequireDefault(_formatter);
var _logSymbols2 = _interopRequireDefault(_logSymbols);
var _table = require("table");
var _table2 = _interopRequireDefault(_table);
var _util = require("postcss-reporter/lib/util");
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var minimalFormatter = (0, _formatter2.default)({
noIcon: true,
noPlugin: true
});
var MARGIN_WIDTHS = 9;
var levelColors = {
info: "blue",
warning: "yellow",
error: "red"
};
function deprecationsFormatter(results) {

@@ -74,2 +86,86 @@ var allDeprecationWarnings = _lodash2.default.flatMap(results, "deprecations");

}, "\n");
}
function logFrom(fromValue) {
if (fromValue.charAt(0) === "<") return fromValue;
return _path2.default.relative(process.cwd(), fromValue).split(_path2.default.sep).join("/");
}
function getMessageWidth(columnWidths) {
if (!process.stdout.isTTY) {
return columnWidths[3];
}
var availableWidth = process.stdout.columns;
var fullWidth = _lodash2.default.sum(_lodash2.default.values(columnWidths));
// If there is no reason to wrap the text, we won't align the last column to the right
if (availableWidth > fullWidth + MARGIN_WIDTHS) {
return columnWidths[3];
}
return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS);
}
function formatter(messages, source) {
if (!messages.length) return "";
var orderedMessages = _lodash2.default.sortBy(messages, function (m) {
return m.line ? 2 : 1;
}, // positionless first
function (m) {
return m.line;
}, function (m) {
return m.column;
});
// Create a list of column widths, needed to calculate
// the size of the message column and if needed wrap it.
var columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 };
var calculateWidths = function calculateWidths(columns) {
_lodash2.default.forOwn(columns, function (value, key) {
columnWidths[key] = Math.max(columnWidths[key], _chalk2.default.stripColor(value).toString().length);
});
return columns;
};
var output = "\n";
if (source) {
output += _chalk2.default.bold.underline(logFrom(source)) + "\n";
}
var cleanedMessages = orderedMessages.map(function (message) {
var location = _util2.default.getLocation(message);
var severity = message.severity;
var row = [location.line || "", location.column || "", _logSymbols2.default[severity] ? _chalk2.default[levelColors[severity]](_logSymbols2.default[severity]) : severity, message.text.replace(/\.$/, "").replace(new RegExp(_lodash2.default.escapeRegExp("(" + message.rule + ")") + "$"), ""), _chalk2.default.yellow(message.rule || "")];
calculateWidths(row);
return row;
});
output += (0, _table2.default)(cleanedMessages, {
border: (0, _table.getBorderCharacters)("void"),
columns: {
0: { alignment: "right", width: columnWidths[0], paddingRight: 0 },
1: { alignment: "left", width: columnWidths[1] },
2: { alignment: "left", width: columnWidths[2] },
3: { alignment: "left", width: getMessageWidth(columnWidths), wrapWord: true },
4: { alignment: "left", width: columnWidths[4], paddingRight: 0 }
},
drawHorizontalLine: function drawHorizontalLine() {
return false;
}
}).split("\n").map(function (el) {
return el.replace(/(\d+)\s+(\d+)/, function (m, p1, p2) {
return _chalk2.default.bold(p1 + ":" + p2);
});
}).join("\n");
return output;
}

@@ -31,3 +31,4 @@ "use strict";

styleSearch: _utils.styleSearch,
validateOptions: _utils.validateOptions
validateOptions: _utils.validateOptions,
cssWordIsVariable: _utils.cssWordIsVariable
};

@@ -34,0 +35,0 @@

@@ -52,6 +52,8 @@ "use strict";

var messages = exports.messages = (0, _utils.ruleMessages)(ruleName, {
expected: function expected(h, v) {
return "Expected \"" + h + "\" to be \"" + v + "\"";
var messages = (0, _utils.ruleMessages)(ruleName, {
expected: function expected(actual, _expected) {
return "Expected \"" + actual + "\" to be \"" + _expected + "\"";
}
});
});
exports.messages = messages;

@@ -8,5 +8,11 @@ "use strict";

exports.default = function (actual) {
exports.default = function (on, options) {
return function (root, result) {
var validOptions = (0, _utils.validateOptions)(result, ruleName, { actual: actual });
var validOptions = (0, _utils.validateOptions)(result, ruleName, { actual: on }, {
actual: options,
possible: {
ignore: ["consecutive-duplicates"]
},
optional: true
});
if (!validOptions) {

@@ -29,2 +35,3 @@ return;

var decls = [];
node.each(function (child) {

@@ -34,5 +41,7 @@ if (child.nodes && child.nodes.length) {

}
if (child.type !== "decl") {
return;
}
var prop = child.prop;

@@ -49,3 +58,9 @@

if (decls.indexOf(prop) !== -1) {
var indexDuplicate = decls.indexOf(prop);
if (indexDuplicate !== -1) {
if ((0, _utils.optionsHaveIgnored)(options, "consecutive-duplicates") && indexDuplicate === decls.length - 1) {
return;
}
(0, _utils.report)({

@@ -58,2 +73,3 @@ message: messages.rejected(prop),

}
decls.push(prop);

@@ -60,0 +76,0 @@ });

@@ -23,2 +23,5 @@ "use strict";

}
if (!rule.nodes) {
return;
}

@@ -25,0 +28,0 @@ var decls = rule.nodes.filter(function (node) {

@@ -18,32 +18,35 @@ "use strict";

root.walkRules(function (rule) {
// Return early if an empty rule
if ((0, _utils.cssStatementHasEmptyBlock)(rule)) {
root.walkAtRules(function (atRule) {
if (atRule.parent === root) {
return;
}
if (!rule.last || rule.last.type !== "decl") {
if (atRule !== atRule.parent.last) {
return;
}
if ((0, _utils.cssStatementHasBlock)(atRule)) {
return;
}
checkLastNode(atRule);
});
var errorIndexOffset = rule.toString().length;
var after = rule.raw("after");
if (after) {
errorIndexOffset -= after.length;
root.walkDecls(function (decl) {
if (decl !== decl.parent.last) {
return;
}
checkLastNode(decl);
});
var errorIndex = void 0;
function checkLastNode(node) {
var message = void 0;
if (expectation === "always") {
if (rule.raws.semicolon) {
if (node.parent.raws.semicolon) {
return;
}
errorIndex = errorIndexOffset - 1;
message = messages.expected;
}
if (expectation === "never") {
if (!rule.raws.semicolon) {
if (!node.parent.raws.semicolon) {
return;
}
errorIndex = errorIndexOffset - 2;
message = messages.rejected;

@@ -54,8 +57,8 @@ }

message: message,
node: rule,
index: errorIndex,
node: node,
index: node.toString().trim().length - 1,
result: result,
ruleName: ruleName
});
});
}
};

@@ -62,0 +65,0 @@ };

@@ -8,3 +8,5 @@ "use strict";

exports.default = function (space, options) {
exports.default = function (space) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var isTab = space === "tab";

@@ -23,3 +25,5 @@ var indentChar = isTab ? "\t" : (0, _lodash.repeat)(" ", space);

ignore: ["value", "param"],
hierarchicalSelectors: [_lodash.isBoolean]
hierarchicalSelectors: [_lodash.isBoolean],
indentInsideParens: ["once", "twice", "once-at-root-twice-in-block"],
indentClosingBrace: [_lodash.isBoolean]
},

@@ -85,5 +89,7 @@ optional: true

// otherwise there's no indentation involved.
if ((0, _utils.cssStatementHasBlock)(node) && after && after.indexOf("\n") !== -1 && after.slice(after.lastIndexOf("\n") + 1) !== expectedWhitespace) {
// And check `indentClosingBrace` to see if it should be indented an extra level.
var closingBraceLevel = options.indentClosingBrace ? nodeLevel + 1 : nodeLevel;
if ((0, _utils.cssStatementHasBlock)(node) && after && after.indexOf("\n") !== -1 && after.slice(after.lastIndexOf("\n") + 1) !== (0, _lodash.repeat)(indentChar, closingBraceLevel)) {
(0, _utils.report)({
message: messages.expected(legibleExpectation(nodeLevel)),
message: messages.expected(legibleExpectation(closingBraceLevel)),
node: node,

@@ -185,3 +191,32 @@ index: node.toString().length - 1,

// Sass maps are ignored to allow for arbitrary indentation
(0, _utils.styleSearch)({ source: source, target: "\n", outsideParens: true }, function (match) {
(0, _utils.styleSearch)({ source: source, target: "\n", outsideParens: !options.indentInsideParens }, function (match) {
var expectedIndentLevel = newlineIndentLevel;
// Modify for paren content and closing paren
if (options.indentInsideParens && match.insideParens) {
var isClosingParen = /^\s*\)/.test(source.slice(match.startIndex));
switch (options.indentInsideParens) {
case "once":
if (isClosingParen && !options.indentClosingBrace) {
expectedIndentLevel -= 1;
}
break;
case "twice":
if (!isClosingParen || options.indentClosingBrace) {
expectedIndentLevel += 1;
}
break;
case "once-at-root-twice-in-block":
if (node.parent === root) {
if (isClosingParen && !options.indentClosingBrace) {
expectedIndentLevel -= 1;
}
break;
}
if (!isClosingParen || options.indentClosingBrace) {
expectedIndentLevel += 1;
}
break;
}
}
// Starting at the index after the newline, we want to

@@ -196,7 +231,7 @@ // check that the whitespace characters (excluding newlines) before the first

if (afterNewlineSpace !== (0, _lodash.repeat)(indentChar, newlineIndentLevel)) {
if (afterNewlineSpace !== (0, _lodash.repeat)(indentChar, expectedIndentLevel)) {
(0, _utils.report)({
message: messages.expected(legibleExpectation(newlineIndentLevel)),
message: messages.expected(legibleExpectation(expectedIndentLevel)),
node: node,
index: match.startIndex + 1,
index: match.startIndex + afterNewlineSpace.length + 1,
result: result,

@@ -203,0 +238,0 @@ ruleName: ruleName

@@ -11,2 +11,6 @@ "use strict";

var _atRuleNameCase = require("./at-rule-name-case");
var _atRuleNameCase2 = _interopRequireDefault(_atRuleNameCase);
var _atRuleEmptyLineBefore = require("./at-rule-empty-line-before");

@@ -204,2 +208,6 @@

var _functionNameCase = require("./function-name-case");
var _functionNameCase2 = _interopRequireDefault(_functionNameCase);
var _functionParenthesesNewlineInside = require("./function-parentheses-newline-inside");

@@ -345,2 +353,6 @@

var _propertyCase = require("./property-case");
var _propertyCase2 = _interopRequireDefault(_propertyCase);
var _propertyNoVendorPrefix = require("./property-no-vendor-prefix");

@@ -442,2 +454,10 @@

var _selectorPseudoClassCase = require("./selector-pseudo-class-case");
var _selectorPseudoClassCase2 = _interopRequireDefault(_selectorPseudoClassCase);
var _selectorPseudoElementCase = require("./selector-pseudo-element-case");
var _selectorPseudoElementCase2 = _interopRequireDefault(_selectorPseudoElementCase);
var _selectorPseudoElementColonNotation = require("./selector-pseudo-element-colon-notation");

@@ -475,2 +495,6 @@

var _unitCase = require("./unit-case");
var _unitCase2 = _interopRequireDefault(_unitCase);
var _unitNoUnknown = require("./unit-no-unknown");

@@ -484,2 +508,6 @@

var _valueKeywordCase = require("./value-keyword-case");
var _valueKeywordCase2 = _interopRequireDefault(_valueKeywordCase);
var _valueListCommaNewlineAfter = require("./value-list-comma-newline-after");

@@ -509,2 +537,3 @@

"at-rule-semicolon-newline-after": _atRuleSemicolonNewlineAfter2.default,
"at-rule-name-case": _atRuleNameCase2.default,
"at-rule-empty-line-before": _atRuleEmptyLineBefore2.default,

@@ -558,2 +587,3 @@ "at-rule-no-vendor-prefix": _atRuleNoVendorPrefix2.default,

"function-max-empty-lines": _functionMaxEmptyLines2.default,
"function-name-case": _functionNameCase2.default,
"function-parentheses-newline-inside": _functionParenthesesNewlineInside2.default,

@@ -594,2 +624,3 @@ "function-parentheses-space-inside": _functionParenthesesSpaceInside2.default,

"property-blacklist": _propertyBlacklist2.default,
"property-case": _propertyCase2.default,
"property-no-vendor-prefix": _propertyNoVendorPrefix2.default,

@@ -619,2 +650,4 @@ "property-unit-blacklist": _propertyUnitBlacklist2.default,

"selector-no-vendor-prefix": _selectorNoVendorPrefix2.default,
"selector-pseudo-class-case": _selectorPseudoClassCase2.default,
"selector-pseudo-element-case": _selectorPseudoElementCase2.default,
"selector-pseudo-element-colon-notation": _selectorPseudoElementColonNotation2.default,

@@ -628,4 +661,6 @@ "selector-root-no-composition": _selectorRootNoComposition2.default,

"unit-blacklist": _unitBlacklist2.default,
"unit-case": _unitCase2.default,
"unit-no-unknown": _unitNoUnknown2.default,
"unit-whitelist": _unitWhitelist2.default,
"value-keyword-case": _valueKeywordCase2.default,
"value-list-comma-newline-after": _valueListCommaNewlineAfter2.default,

@@ -632,0 +667,0 @@ "value-list-comma-newline-before": _valueListCommaNewlineBefore2.default,

@@ -67,3 +67,8 @@ "use strict";

var valueWithZero = value.slice(valueWithZeroStart, valueWithZeroEnd);
var parsedValue = _postcssValueParser2.default.unit(valueWithZero);
if (parsedValue && parsedValue.unit && ignoredUnits.has(parsedValue.unit)) {
return;
}
// Add the indexes to ignorableIndexes so the same value will not

@@ -92,2 +97,5 @@ // be checked multiple times.

}
if ((0, _utils.isKnownUnit)(valueWithZero.slice(-1))) {
return 1;
}
return 0;

@@ -114,7 +122,15 @@ }();

var _postcssValueParser = require("postcss-value-parser");
var _postcssValueParser2 = _interopRequireDefault(_postcssValueParser);
var _utils = require("../../utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var ruleName = exports.ruleName = "number-zero-length-no-unit";
var messages = exports.messages = (0, _utils.ruleMessages)(ruleName, {
rejected: "Unexpected unit on zero length number"
});
});
var ignoredUnits = new Set(["dpcm", "dppx", "dpi", "kHz", "Hz", "s", "ms", "%"]);

@@ -23,2 +23,6 @@ "use strict";

if ((0, _utils.cssWordIsVariable)(prop)) {
return;
}
if ((0, _utils.matchesStringOrRegExp)(_postcss.vendor.unprefixed(prop), blacklist)) {

@@ -25,0 +29,0 @@ (0, _utils.report)({

@@ -27,8 +27,14 @@ "use strict";

}
if (node.type !== "word") {
if (node.type !== "word" || (0, _utils.cssWordIsVariable)(node.value)) {
return;
}
var unit = _postcssValueParser2.default.unit(node.value).unit;
var parsedUnit = _postcssValueParser2.default.unit(node.value);
if (!parsedUnit) {
return;
}
var unit = parsedUnit.unit;
if (!unit || blacklist.indexOf(unit) === -1) {

@@ -35,0 +41,0 @@ return;

@@ -27,8 +27,14 @@ "use strict";

}
if (node.type !== "word") {
if (node.type !== "word" || (0, _utils.cssWordIsVariable)(node.value)) {
return;
}
var unit = _postcssValueParser2.default.unit(node.value).unit;
var parsedUnit = _postcssValueParser2.default.unit(node.value);
if (!parsedUnit) {
return;
}
var unit = parsedUnit.unit;
if (!unit || whitelist.indexOf(unit) !== -1) {

@@ -35,0 +41,0 @@ return;

@@ -97,3 +97,3 @@ "use strict";

configOverrides: configOverrides
})).process(code, postcssProcessOptions).then(handleResult);
})).process(code, postcssProcessOptions).then(handleResult).catch(cssSyntaxError);

@@ -136,2 +136,22 @@ function handleResult(postcssResult) {

}
function cssSyntaxError(error) {
if (error.name !== "CssSyntaxError") {
throw error;
}
return {
source: error.file || "<input css 1>",
deprecations: [],
invalidOptionWarnings: [],
errored: true,
warnings: [{
line: error.line,
column: error.column,
rule: error.name,
severity: "error",
text: error.reason + " (" + error.name + ")"
}]
};
}
}

@@ -138,0 +158,0 @@ };

@@ -24,6 +24,10 @@ "use strict";

// Absolute length units
"px", "mm", "cm", "in", "pt", "pc",
"px", "mm", "cm", "in", "pt", "pc", "q",
// Time length units
"s", "ms",
// Angle
"deg", "grad", "turn", "rad"]);
"deg", "grad", "turn", "rad",
// Frequency
"Hz", "kHz",
// Resolution
"dpi", "dpcm", "dppx"]);
# Developer guide
- [Rules](/docs/developer-guide/rules.md) - adding new rules to stylelint.
- [Rules](/docs/developer-guide/rules.md) - working on stylelint's rules.
- [Plugins](/docs/developer-guide/plugins.md) - writing your own plugins.
- [Formatters](/docs/developer-guide/formatters.md) - writing your own formatters.
- [Rule Testers](/docs/developer-guide/rule-testers.md) - using and writing rule testers.
- [Rule testers](/docs/developer-guide/rule-testers.md) - using and writing rule testers.
- [Benchmarks](/docs/developer-guide/benchmarks.md) - running benchmarks on rules.

@@ -25,3 +25,3 @@ # Working on rules

Have a look at the [rules guide](/docs/user-guide/rules.md) to familiarize yourself the rule naming conventions. In particular, the section [about rule names](/docs/user-guide/rules.md#about-rule-names).
Have a look at the [rules user guide](/docs/user-guide/about-rules.md) to familiarize yourself the rule naming conventions.

@@ -28,0 +28,0 @@ We take care to ensure that all the rules are named accurately and consistently. Our goals in that effort are to ensure that rules are easy to find and understand, and to prevent us from wanting to change the name later.

# User guide
## Usage
- [The CLI](/docs/user-guide/cli.md) - options, examples and exit codes for using the CLI.
- [Configuration](/docs/user-guide/configuration.md) - how to configure stylelint.
- [About rules](/docs/user-guide/about-rules.md) - an explanation of rule names and how rules work together.
- [Rules](/docs/user-guide/rules.md) - a list of stylelint's rules.
- [Plugins](/docs/user-guide/plugins.md) - a list of community plugins.
- [Example config](/docs/user-guide/example-config.md) - an example config that you can use as a blueprint for your own.
- [The CLI](/docs/user-guide/cli.md) - examples and exit codes for using the CLI.
- [The Node API](/docs/user-guide/node-api.md) - options and examples for using the Node API.
- [The PostCSS Plugin](/docs/user-guide/postcss-plugin.md) - how to use the PostCSS plugin, either via a runner like `gulp-postcss` or via the PostCSS Node API.
## Configuration
- [Configuration](/docs/user-guide/configuration.md) - how to configure stylelint.
- [Rules](/docs/user-guide/rules.md) - a list of all the rules that are available to you, and an explanation of the naming convention.
- [Example Config](/docs/user-guide/example-config.md) - an example config that you can use as a foundation for crafting your own config.
- [Plugins](/docs/user-guide/plugins.md) - a brief introduction to plugins, followed by a list of what's available.
- [CSS Processors](/docs/user-guide/css-processors.md) - how to use the linter with CSS processors, like SCSS or PostCSS plugins.
- [Complementary Tools](/docs/user-guide/complementary-tools.md) - editor plugins (e.g. Atom and Sublime) and other linters.
- [The PostCSS plugin](/docs/user-guide/postcss-plugin.md) - options and examples for using the PostCSS plugin.
- [CSS processors](/docs/user-guide/css-processors.md) - how to use the linter with a CSS processor, like SCSS or PostCSS plugins.
- [Complementary tools](/docs/user-guide/complementary-tools.md) - list of editor plugins, build tool plugins and other tools.
- [FAQ](/docs/user-guide/faq.md) - frequently asked questions about using and configuring stylelint.

@@ -41,2 +41,8 @@ # The stylelint CLI

## Syntax errors
The CLI informs you about syntax errors in your CSS.
It uses the same format as it uses for linting warnings.
The error name is `CssSyntaxError`.
## Exit codes

@@ -43,0 +49,0 @@

@@ -5,16 +5,13 @@ # Complementary tools

- [linter-stylelint](https://github.com/AtomLinter/linter-stylelint) - An Atom Linter plugin for stylelint.
- [SublimeLinter-contrib-stylelint](https://github.com/kungfusheep/SublimeLinter-contrib-stylelint) - A Sublime Text plugin for stylelint.
- [vscode-stylelint](https://github.com/shinnn/vscode-stylelint) - A Visual Studio Code extension for stylelint.
- [linter-stylelint](https://github.com/AtomLinter/linter-stylelint) - an Atom Linter plugin for stylelint.
- [SublimeLinter-contrib-stylelint](https://github.com/kungfusheep/SublimeLinter-contrib-stylelint) - a Sublime Text plugin for stylelint.
- [vscode-stylelint](https://github.com/shinnn/vscode-stylelint) - a Visual Studio Code extension for stylelint.
## Runners/loaders
## Build tool plugins
- [gulp-stylelint](https://github.com/olegskl/gulp-stylelint) - A gulp plugin for stylelint.
- [stylelint-loader](https://github.com/adrianhall/stylelint-loader) - A webpack loader for stylelint.
- [stylelint-webpack-plugin](https://github.com/vieron/stylelint-webpack-plugin) - A webpack plugin for stylelint.
- [gulp-stylelint](https://github.com/olegskl/gulp-stylelint) - a gulp plugin for stylelint.
- [stylelint-webpack-plugin](https://github.com/vieron/stylelint-webpack-plugin) - a webpack plugin for stylelint.
## Other linters & formatters
## Autocorrect
- [cssfmt](https://github.com/morishitter/cssfmt) - A tool that automatically formats CSS and SCSS source code.
- [postcss-bem-linter](https://github.com/postcss/postcss-bem-linter) - A BEM linter for CSS.
- [stylehacks](https://github.com/ben-eb/stylehacks) - Detect/remove browser hacks from CSS files.
- [stylefmt](https://github.com/morishitter/stylefmt) - automatically formats CSS and SCSS source code based on your stylelint configuration.

@@ -5,6 +5,4 @@ # Configuration

## Loading Configuration
## Loading the configuration object
For the Node API and PostCSS plugin, you can pass a config directly or find and load it. For the CLI, you must find and load it.
Finding and loading of your configuration object is done with [cosmiconfig](https://github.com/davidtheclark/cosmiconfig). Starting from the current working directory, it will look for the following possible sources, in this order:

@@ -20,5 +18,5 @@

The configuration search can be short-circuited by passing a `--config` CLI argument specifying the path to your configuration file.
The configuration search can be short-circuited by using either the `config` or `configFile` options.
## The Configuration Object
## The configuration object

@@ -29,5 +27,5 @@ The configuration object can have the following properties. Only `rules` is required.

[Rules](/docs/user-guide/rules.md) determine what the linter looks for and complains about. *No rules are turned on by default*, so this is where you turn on everything you want to check.
Rules determine what the linter looks for and complains about. There are [over 100](/docs/user-guide/rules.md) built into stylelint. *No rules are turned on by default*, so this is where you turn on everything you want to check. All the rules must be explicitly configured as *there are no default values*.
**The `rules` property is an object whose keys are rule names and values are rule configurations.** Each rule configuration fits one of the following formats:
The `rules` property is *an object whose keys are rule names and values are rule configurations*. Each rule configuration fits one of the following formats:

@@ -41,8 +39,10 @@ - a single value (the primary option)

"rules": {
"block-no-empty": null,
"color-no-invalid-hex": true,
"declaration-colon-space-after": "always",
"max-empty-lines": 2,
"indentation": ["tab", {
"except": ["value"]
}]
}],
"max-empty-lines": 2,
"unit-whitelist": ["em", "rem", "%", "s"]
}

@@ -52,5 +52,48 @@ }

Specifying a primary option will turn a rule on.
#### Turning rules off from within your CSS
Rules can be temporarily turned off by using special comments in your CSS. For example, you can either turn all the rules off:
```css
/* stylelint-disable */
a {}
/* stylelint-enable */
```
Or you can turn off individual rules:
```css
/* stylelint-disable selector-no-id, declaration-no-important */
#id {
color: pink !important;
}
/* stylelint-enable */
```
And you can turn off rules for individual lines only, after which you do not need to explicitly re-enable them:
```css
#id { /* stylelint-disable-line */
color: pink !important; /* stylelint-disable-line declaration-no-important */
}
```
Complex, overlapping disabling & enabling patterns are supported:
```css
/* stylelint-disable */
/* stylelint-enable foo */
/* stylelint-disable foo */
/* stylelint-enable */
/* stylelint-disable foo, bar */
/* stylelint-disable baz */
/* stylelint-enable baz, bar */
/* stylelint-enable foo */
```
#### Severities: error & warning
By default, all rules have an "error"-level severity.
By default, all rules have an `"error"`-level severity.

@@ -78,6 +121,2 @@ To downgrade any rule use the secondary option `severity`. The available values for `severity` are:

##### Legacy Numbered Severities
As of v4, the legacy numbered severities system is no longer supported. Please upgrade your config to use the syntax documented here.
#### Custom Messages

@@ -95,5 +134,5 @@

"indentation": [ 2, {
"severity": "warning",
"except": ["param"],
"message": "Please use 2 spaces for indentation. Tabs make The Architect grumpy."
"ignore": ["block"],
"message": "Please use 2 spaces for indentation. Tabs make The Architect grumpy.",
"severity": "warning"
} ]

@@ -111,3 +150,3 @@ }

For example, extending the [`stylelint-config-standard`](https://github.com/stylelint/stylelint-config-standard) and then changing indentation to tabs and turning off the number-leading-zero rule:
For example, extending the [`stylelint-config-standard`](https://github.com/stylelint/stylelint-config-standard) and then changing indentation to tabs and turning off the `number-leading-zero` rule:

@@ -119,3 +158,3 @@ ```json

"indentation": "tab",
"number-leading-zero": null,
"number-leading-zero": null
}

@@ -125,3 +164,3 @@ }

Or starting with `stylelint-config-standard`, then extending layering `myExtendableConfig` on top of that, and then overriding the indentation rule:
Or starting with `stylelint-config-standard`, then layering `myExtendableConfig` on top of that, and then overriding the indentation rule:

@@ -131,7 +170,7 @@ ```json

"extends": [
"./myExtendableConfig",
"stylelint-config-standard"
"stylelint-config-standard",
"./myExtendableConfig"
],
"rules": {
"indentation": "tab",
"indentation": "tab"
}

@@ -141,3 +180,3 @@ }

**The value of `"extends"` is a "locater" (or an array of "locaters") that is ultimately `require()`d, so can fit whatever format works with Node's `require.resolve()` algorithm.** That means the a "locater" can be:
The value of `"extends"` is a "locater" (or an array of "locaters") that is ultimately `require()`d, so can fit whatever format works with Node's `require.resolve()` algorithm. That means the a "locater" can be:

@@ -152,5 +191,5 @@ - The name of a module in `node_modules` (e.g. `stylelint-config-standard`; that module's `main` file must be a valid JSON configuration)

[Plugins](/docs/user-guide/plugins.md) are userland rules that support *non-standard* CSS features, or very specific use cases. To use one, add a `"plugins"` array to your config, containing "locaters" identifying the plugins you want to use. As with `extends`, above, a "locater" can be either an npm module name, an absolute path, or a path relative to the invoking configuration file.
Plugins are rules built by the community that support methodologies, toolsets, *non-standard* CSS features, or very specific use cases. To use one, add a `"plugins"` array to your config, containing "locaters" identifying the plugins you want to use. As with `extends`, above, a "locater" can be either an npm module name, an absolute path, or a path relative to the invoking configuration file.
Once the plugin is declared, within your `"rules"` object you can add settings for the plugin's rule just like any standard rule. You will have to look at the plugin's documentation to know what the rule name should be.
Once the plugin is declared, within your `"rules"` object *you'll need to add options* for the plugin's rule just like any standard rule. You will have to look at the plugin's documentation to know what the rule name should be.

@@ -160,6 +199,6 @@ ```json

"plugins": [
"../special-rule.js",
"../special-rule.js"
],
"rules": {
"special-rule": "everything",
"special-rule": "everything"
},

@@ -180,210 +219,1 @@ }

The `ignoreFiles` property is stripped from extended configs: only the root-level config can ignore files.
## Configuring rules
[Rules](/docs/user-guide/rules.md) are built into the linter and focus on *standard* CSS. They are configured within the `rules` key of the config.
### Turning rules on and off
Each rule can be turned off or on:
- `null` - turn the rule off.
- `true|options` - turn the rule on.
All turned-on rules error by default. You can reduce the severity of a rule, to a warning, by adding `"severity": "warning"` to its secondary options.
An example of turning rules on and off:
```js
{
"rules": {
"rule-no-single-line": null, // turn rule off
"declaration-no-important": true, // turn rule on
"block-no-empty": [ true, { "severity": "warning" } ], // turn rule on as warning
"indentation": "tabs" // turn on a rule that has options
}
}
```
Rules can be temporarily turned off by using special comments in your CSS. For example, you can either turn all the rules off:
```css
/* stylelint-disable */
a {}
/* stylelint-enable */
```
Or you can turn off individual rules:
```css
/* stylelint-disable selector-no-id, declaration-no-important */
#id {
color: pink !important;
}
/* stylelint-enable */
```
And you can turn off rules for individual lines only, after which you do not need to explicitly re-enable them:
```css
#id { /* stylelint-disable-line */
color: pink !important; /* stylelint-disable-line declaration-no-important */
}
```
Complex, overlapping disabling & enabling patterns are supported:
```css
/* stylelint-disable */
/* stylelint-enable foo */
/* stylelint-disable foo */
/* stylelint-enable */
/* stylelint-disable foo, bar */
/* stylelint-disable baz */
/* stylelint-enable baz, bar */
/* stylelint-enable foo */
```
### Configuring options
Only the `*-no-*` rules don't expect options. All the other rules must be explicitly configured as *there are no default values*.
An example of explicitly configuring the options for three rules:
```js
{
"rules": {
"indentation": "tab", {
"except": ["value"],
}],
"declaration-colon-space-before": "never",
"number-leading-zero": "always",
}
}
```
Remember that any rule can be downgraded to "warning"-level severity by adding `"severity": "warning"` as a secondary option.
### Rules work together
The `*-before` and `*-after` whitespace rules can be used together to enforce strict conventions.
Say you want to enforce no space before and a single space after the colon in every declaration:
```css
a { color: pink; }
/** ↑
* No space before and a single space after this colon */
```
You can enforce that with:
```js
"declaration-colon-space-after": "always",
"declaration-colon-space-before": "never",
```
Some *things* (e.g. declaration blocks and value lists) can span more than one line. In these cases `newline` rules and extra options can be used to provide flexibility.
For example, this is the complete set of `value-list-comma-*` rules and their options:
- `value-list-comma-space-after`: `"always"|"never"|"always-single-line"|"never-single-line"`
- `value-list-comma-space-before`: `"always"|"never"|"always-single-line"|"never-single-line"`
- `value-list-comma-newline-after`: `"always"|"always-multi-line|"never-multi-line"`
- `value-list-comma-newline-before`: `"always"|"always-multi-line"|"never-multi-line"`
Where `*-multi-line` and `*-single-line` are in reference to the value list (the *thing*). For example, given:
```css
a,
b {
color: red;
font-family: sans, serif, monospace; /* single line value list */
} ↑ ↑
/** ↑ ↑
* The value list start here and ends here */
```
There is only a single-line value list in this example. The selector is multi-line, as is the declaration block and, as such, also the rule. But the value list isn't and that is what the `*-multi-line` and `*-single-line` refer to in the context of this rule.
#### Example A
Say you only want to allow single-line value lists. And you want to enforce no space before and a single space after the commas:
```css
a {
font-family: sans, serif, monospace;
box-shadow: 1px 1px 1px red, 2px 2px 1px 1px blue inset, 2px 2px 1px 2px blue inset;
}
```
You can enforce that with:
```js
"value-list-comma-space-after": "always",
"value-list-comma-space-before": "never",
```
#### Example B
Say you want to allow both single-line and multi-line value lists. You want there to be a single space after the commas in the single-line lists and no space before the commas in both the single-line and multi-line lists:
```css
a {
font-family: sans, serif, monospace; /* single-line value list with space after, but no space before */
box-shadow: 1px 1px 1px red, /* multi-line value list ... */
2px 2px 1px 1px blue inset, /* ... with newline after, ... */
2px 2px 1px 2px blue inset; /* ... but no space before */
}
```
You can enforce that with:
```js
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
```
#### Example C
Say you want to allow both single-line and multi-line value lists. You want there to be no space before the commas in the single-line lists and always a space after the commas in both lists:
```css
a {
font-family: sans, serif, monospace;
box-shadow: 1px 1px 1px red
, 2px 2px 1px 1px blue inset
, 2px 2px 1px 2px blue inset;
}
```
You can enforce that with:
```js
"value-list-comma-newline-before": "always-multi-line",
"value-list-comma-space-after": "always",
"value-list-comma-space-before": "never-single-line",
```
#### Example D
Lastly, the rules are flexible enough to enforce entirely different conventions for single-line and multi-line lists. Say you want to allow both single-line and multi-line value lists. You want the single-line lists to have a single space before and after the colons. Whereas you want the multi-line lists to have a single newline before the commas, but no space after:
```css
a {
font-family: sans , serif , monospace; /* single-line list with a single space before and after the comma */
box-shadow: 1px 1px 1px red /* multi-line list ... */
,2px 2px 1px 1px blue inset /* ... with newline before, ... */
,2px 2px 1px 2px blue inset; /* ... but no space after the comma */
}
```
You can enforce that with:
```js
"value-list-comma-newline-after": "never-multi-line",
"value-list-comma-newline-before": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "always-single-line",
```

@@ -17,3 +17,3 @@ # CSS processors

- SCSS (using [`postcss-scss`](https://github.com/postcss/postcss-scss))
- Less (using [postcss-less](https://github.com/webschik/postcss-less))
- Less (using [`postcss-less`](https://github.com/webschik/postcss-less))
- SugarSS (using [`sugarss`](https://github.com/postcss/sugarss))

@@ -20,0 +20,0 @@

# Example config
This example config lists all of the [rules](/docs/user-guide/rules.md) and their primary options. You can remove ([or turn off](./configuration.md#configuring-rules)) the rules you don't want and edit the primary option of each rule to your liking.
This example config lists all of the [rules](/docs/user-guide/rules.md) and their primary options. You can remove ([or turn off](/docs/user-guide/configuration.md#rules)) the rules you don't want and edit the primary option of each rule to your liking.
You might want to learn a little about [the conventions](/docs/user-guide/rules.md#about-rule-names) the rule names follow, to get a better idea of what each rule does.
You might want to learn a little about [how rules are named and how they work together](/docs/user-guide/about-rules.md), to get a better idea of what each rule does.

@@ -11,2 +11,3 @@ ```json

"at-rule-empty-line-before": "always"|"never",
"at-rule-name-case": "lower"|"upper",
"at-rule-no-vendor-prefix": true,

@@ -59,2 +60,3 @@ "at-rule-semicolon-newline-after": "always",

"function-linear-gradient-no-nonstandard-direction": true,
"function-name-case": "lower"|"upper",
"function-parentheses-newline-inside": "always"|"always-multi-line"|"never-multi-line",

@@ -95,2 +97,3 @@ "function-parentheses-space-inside": "always"|"never"|"always-single-line"|"never-single-line",

"property-blacklist": string|[],
"property-case": "lower"|"upper",
"property-no-vendor-prefix": true,

@@ -119,2 +122,4 @@ "property-unit-blacklist": {},

"selector-no-vendor-prefix": true,
"selector-pseudo-class-case": "lower"|"upper",
"selector-pseudo-element-case": "lower"|"upper",
"selector-pseudo-element-colon-notation": "single"|"double",

@@ -127,4 +132,6 @@ "selector-root-no-composition": true,

"unit-blacklist": string|[],
"unit-case": "single"|"double",
"unit-no-unknown": true,
"unit-whitelist": string|[],
"value-keyword-case": "lower"|"upper",
"value-list-comma-newline-after": "always"|"always-multi-line"|"never-multi-line",

@@ -131,0 +138,0 @@ "value-list-comma-newline-before": "always"|"always-multi-line"|"never-multi-line",

@@ -14,10 +14,4 @@ # The stylelint Node API

Though both `files` and `css` are "optional," you *must* have one and *cannot* have both. All other options are optional._
Though both `files` and `code` are "optional", you *must* have one and *cannot* have both. All other options are optional.
### `files`
A file glob, or array of file globs. Ultimately passed to [node-glob](https://github.com/isaacs/node-glob) to figure out what files you want to lint.
`node_modules` and `bower_components` are always ignored.
### `code`

@@ -33,16 +27,14 @@

### `formatter`
### `config`
`"json"`, `"string"`, or a function. Default is `"json"`.
A [stylelint configuration object](/docs/user-guide/configuration.md).
Describes the formatter that you would like to use to format your results. `"json"` outputs JSON; `"string"` outputs a human-legible error report.
If no `config` or `configFile` is passed, stylelint will look for a `.stylelintrc` configuration file.
If you pass a function, it must fit the signature described in the [Developer Guide](/docs/developer-guide/formatters.md).
### `configBasedir`
### `config`
An absolute path to the directory that relative paths defining `extends` and `plugins` are *relative to*.
A [stylelint configuration object](/docs/user-guide/configuration.md).
If the `config` object passed uses relative paths for `extends` or `plugins`, you are going to have to pass a `configBasedir`. If not, you do not need this.
If no `config` is passed, stylelint will look for a `.stylelintrc` configuration file.
### `configFile`

@@ -54,8 +46,2 @@

### `configBasedir`
An absolute path to the directory that relative paths defining `extends` and `plugins` are *relative to*.
If the `config` object passed uses relative paths for `extends` or `plugins`, you are going to have to pass a `configBasedir`. If not, you do not need this.
### `configOverrides`

@@ -67,2 +53,16 @@

### `files`
A file glob, or array of file globs. Ultimately passed to [node-glob](https://github.com/isaacs/node-glob) to figure out what files you want to lint.
`node_modules` and `bower_components` are always ignored.
### `formatter`
Options: `"json"|"string"|"verbose"`, or a function. Default is `"json"`.
Specify the formatter that you would like to use to format your results.
If you pass a function, it must fit the signature described in the [Developer Guide](/docs/developer-guide/formatters.md).
### `syntax`

@@ -78,2 +78,6 @@

### `errored`
Boolean. If `true`, at least one rule with an "error"-level severity registered a warning.
### `output`

@@ -83,5 +87,5 @@

### `errored`
### `postcssResults`
Boolean. If `true`, at least one rule with an "error"-level severity registered a warning.
An array containing all the [PostCSS LazyResults](https://github.com/postcss/postcss/blob/master/docs/api.md#lazyresult-class) that were accumulated during processing.

@@ -92,5 +96,6 @@ ### `results`

### `postcssResults`
## Syntax errors
An array containing all the [PostCSS LazyResults](https://github.com/postcss/postcss/blob/master/docs/api.md#lazyresult-class) that were accumulated during processing.
`stylelint.lint()` does not reject the Promise when your CSS contains syntax errors.
It resolves with an object (see [The returned promise](#the-returned-promise)) that contains information about the syntax error.

@@ -103,4 +108,4 @@ ## Usage examples

stylelint.lint({
files: "all/my/stylesheets/*.css",
config: myConfig
config: myConfig,
files: "all/my/stylesheets/*.css"
})

@@ -121,5 +126,5 @@ .then(function(data) {

stylelint.lint({
files: "all/my/stylesheets/*.css",
config: myConfig,
configBasedir: path.join(__dirname, "configs")
configBasedir: path.join(__dirname, "configs"),
files: "all/my/stylesheets/*.css"
}).then(function() { .. });

@@ -142,8 +147,9 @@ ```

stylelint.lint({
config: myConfig,
files: "all/my/stylesheets/*.scss",
config: myConfig,
syntax: "scss",
formatter: function(stylelintResults) { .. }
formatter: function(stylelintResults) { .. },
syntax: "scss"
}).then(function() { .. });
```
The same pattern can be used to read Less or [SugarSS](https://github.com/postcss/sugarss) syntax.
# Plugins
Plugins are rules built by the community that support methodologies, toolsets, *non-standard* CSS features, or very specific use cases.
Plugins are rules built by the community that support methodologies, toolsets, *non-standard* CSS features, or very specific use cases. Their names are prefixed with "stylelint".
Plugins share all the same traits and conventions as [rules](/docs/user-guide/rules.md), except their names are prefixed within "stylelint".
## Methodologies
## Selector
### Selector
- [`stylelint-selector-bem-pattern`](https://github.com/davidtheclark/stylelint-selector-bem-pattern): Specify a BEM pattern for selectors (incorporates [postcss-bem-linter](https://github.com/postcss/postcss-bem-linter).)
- [`stylelint-selector-bem-pattern`](https://github.com/davidtheclark/stylelint-selector-bem-pattern): Specify a BEM pattern for selectors (incorporates [postcss-bem-linter](https://github.com/postcss/postcss-bem-linter)).
## Declaration
## Non-standard syntax
- [`stylelint-declaration-use-variable`](https://github.com/sh-waqar/stylelint-declaration-use-variable): Specify a property for which you want scss variable to be used.
### SCSS
#### Declaration
- [`stylelint-declaration-use-variable`](https://github.com/sh-waqar/stylelint-declaration-use-variable): Specify properties for which a SCSS variable must be used.

@@ -1,5 +0,7 @@

# The stylelint PostCSS Plugin
# The stylelint PostCSS plugin
As with any other [PostCSS plugin](https://github.com/postcss/postcss#plugins), you can use stylelint's PostCSS plugin either with a PostCSS runner -- such as [`gulp-postcss`](https://github.com/postcss/gulp-postcss), [`grunt-postcss`](https://github.com/nDmitry/grunt-postcss) and [`postcss-loader`](https://github.com/postcss/postcss-loader) -- or with the PostCSS JS API directly.
As with any other [PostCSS plugin](https://github.com/postcss/postcss#plugins), you can use stylelint's PostCSS plugin either with a PostCSS runner -- such as [`grunt-postcss`](https://github.com/nDmitry/grunt-postcss) -- or with the PostCSS JS API directly.
If a dedicated stylelint task runner plugin [is available](/docs/user-guide/complementary-tools.md) (e.g. `gulp-stylelint`) we recommend you use that rather than this plugin.
## Options

@@ -13,7 +15,7 @@

If no `config` is passed, stylelint will look for a `.stylelintrc` configuration file.
If no `config` or `configFile` is passed, stylelint will look for a `.stylelintrc` configuration file.
### `configFile`
The path to a JSON, YAML, or JS file that contains your [stylelint configuration object](/docs/user-guide/configuration.md).
The path to a JSON, YAML, or JS file that contains your [stylelint configuration object](/docs/user-guide/configuration.md).

@@ -39,21 +41,45 @@ It should be either absolute or relative to the directory that your process is running from (`process.cwd()`). We'd recommend absolute.

We recommend you lint your CSS before applying any transformations. You can do this by either placing stylelint at the beginning of your plugin pipeline, using a plugin like [`postcss-import`](https://github.com/postcss/postcss-import) or [`postcss-easy-import`](https://github.com/TrySound/postcss-easy-import) to lint the your files before any transformations, or by creating a separate lint process that is independent of your build one.
We recommend you lint your CSS before applying any transformations. You can do this by either:
You'll also need to use a reporter. *The stylelint plugin registers warnings via PostCSS*. Therefore, you'll want to use it with a PostCSS runner that prints warnings (e.g. [`gulp-postcss`](https://github.com/postcss/gulp-postcss)) or another PostCSS plugin whose purpose is to format and print warnings (e.g. [`postcss-reporter`](https://github.com/postcss/postcss-reporter)).
- creating a separate lint task that is independent of your build one.
- using the [`plugins` option](https://github.com/postcss/postcss-import#plugins) of [`postcss-import`](https://github.com/postcss/postcss-import) or [`postcss-easy-import`](https://github.com/TrySound/postcss-easy-import) to lint the your files before any transformations.
- placing stylelint at the beginning of your plugin pipeline.
You'll also need to use a reporter. *The stylelint plugin registers warnings via PostCSS*. Therefore, you'll want to use it with a PostCSS runner that prints warnings (e.g. [`grunt-postcss`](https://github.com/nDmitry/grunt-postcss)) or another PostCSS plugin whose purpose is to format and print warnings (e.g. [`postcss-reporter`](https://github.com/postcss/postcss-reporter)).
### Example A
Using the plugin with [`gulp-postcss`](https://github.com/postcss/gulp-postcss), and as a separate lint task:
Using the plugin with [`grunt-postcss`](https://github.com/nDmitry/grunt-postcss), and as a separate lint task:
```js
var postcss = require("gulp-postcss")
var reporter = require("postcss-reporter")
var stylelint = require("stylelint")
gulp.task("lint:css", function () {
return gulp.src("src/**/*.css")
.pipe(postcss([
stylelint({ /* your options */ }),
reporter({ clearMessages: true }),
]))
grunt.initConfig({
postcss: {
// build task (this example uses postcss-import and autoprefixer)
build: {
options: {
map: true,
processors: [
require("postcss-import"),
require("autoprefixer")({browsers: "last 2 versions"})
]
},
// start at the entry file
dist: {
src: 'css/entry.css'
}
},
// separate lint task
lint: {
options: {
processors: [
require("stylelint")({ /* your options */ }),
require("postcss-reporter")({ clearMessages: true })
]
},
// lint all the .css files in the css directory
dist: {
src: 'css/**/*.css'
}
}
}
})

@@ -64,22 +90,25 @@ ```

Using the plugin within [`postcss-import`](https://github.com/postcss/postcss-import) or [`postcss-easy-import`](https://github.com/TrySound/postcss-easy-import), as part of the build task:
Using the plugin with [`grunt-postcss`](https://github.com/nDmitry/grunt-postcss), but within [`postcss-import`](https://github.com/postcss/postcss-import) (using the its `plugins` option) as part of the build task:
```js
var easyImport = require("postcss-easy-import")
var postcss = require("gulp-postcss")
var reporter = require("postcss-reporter")
var stylelint = require("stylelint")
gulp.task("build:css", function () {
return gulp.src("src/main.css")
.pipe(postcss([
stylelint({ /* your options */ })
easyImport({
plugins: [
stylelint({ /* your options */ })
]
})
/* other plugins... */
reporter({ clearMessages: true })
]))
grunt.initConfig({
postcss: {
// this example uses postcss-cssnext
options: {
map: true,
processors: [
require("postcss-import")({
plugins: [
require("stylelint")({ /* your options */ })
]
}),
require("postcss-cssnext")
require("postcss-reporter")({ clearMessages: true })
]
},
// start at the entry file
dist: {
src: 'css/entry.css'
}
}
})

@@ -90,3 +119,3 @@ ```

Using the plugin with [`gulp-postcss`](https://github.com/postcss/gulp-postcss) and [`postcss-less`](https://github.com/webschik/postcss-less) to lint Less, and as part of the build task:
Using the plugin to lint Less using the PostCSS JS API and [`postcss-less`](https://github.com/webschik/postcss-less).

@@ -96,27 +125,4 @@ *Note: the stylelint PostCSS plugin, unlike the stylelint CLI and node API, doesn't have a `syntax` option. Instead, the syntax must be set within the [PostCSS options](https://github.com/postcss/postcss#options) as there can only be one parser/syntax in a pipeline.*

```js
var postcss = require("gulp-postcss")
var reporter = require("postcss-reporter")
var fs = require("fs")
var less = require("postcss-less")
var stylelint = require("stylelint")
gulp.task("build:less", function () {
return gulp.src("src/**/*.less")
.pipe(postcss([
stylelint({ /* your options */ }),
/* other plugins... */
reporter({ clearMessages: true }),
], {
syntax: less
}))
})
```
The same pattern can be used to read SCSS or [SugarSS](https://github.com/postcss/sugarss) syntax.
### Example D
Using the plugin with the PostCSS JS API:
```js
var fs = require("fs")
var postcss = require("postcss")

@@ -133,3 +139,6 @@ var reporter = require("postcss-reporter")

])
.process(css, { from: "input.css" })
.process(css, {
from: "input.css",
syntax: less
})
.then()

@@ -139,5 +148,2 @@ .catch(err => console.error(err.stack))

## PostCSS version compatibility
- Versions `1.0.0+` of the linter are compatible with PostCSS `5.0.2+`.
- Versions `0.8.0 and below` of the linter are compatible with PostCSS `4.x`.
The same pattern can be used to lint SCSS or [SugarSS](https://github.com/postcss/sugarss) syntax.
# Rules
There are over a hundred rules built into stylelint. They all focus on *standard css* and aim to be valuable to the majority of users. In addition to these rules there are [plugins](/docs/user-guide/plugins.md), which are rules built by the community that support methodologies, toolsets, *non-standard* CSS features, or very specific use cases.
Rules focus on *standard css* and aim to be valuable to the majority of users. In addition to these rules there are [plugins](/docs/user-guide/plugins.md), which are rules built by the community that support methodologies, toolsets, *non-standard* CSS features, or very specific use cases. Don't forget to look at the list of [plugins](/docs/user-guide/plugins.md) for more ways to lint.
Every rule and plugin is standalone and turned off by default. None of the rules or plugins have default values for their options.
Every rule is standalone and turned off by default. None of the rules have default values for their options.

@@ -11,4 +11,2 @@ ## List of rules

Don't forget to look at the list of [plugins](/docs/user-guide/plugins.md) for more ways to lint.
### Color

@@ -40,2 +38,3 @@

- [`function-max-empty-lines`](../../src/rules/function-max-empty-lines/README.md): Limit the number of adjacent empty lines within functions.
- [`function-name-case`](../../src/rules/function-name-case/README.md): Specify lowercase or uppercase for function names.
- [`function-parentheses-newline-inside`](../../src/rules/function-parentheses-newline-inside/README.md): Require a newline or disallow whitespace on the inside of the parentheses of functions.

@@ -67,2 +66,3 @@ - [`function-parentheses-space-inside`](../../src/rules/function-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses of functions.

- [`unit-blacklist`](../../src/rules/unit-blacklist/README.md): Specify a blacklist of disallowed units.
- [`unit-case`](../../src/rules/unit-case/README.md): Specify lowercase or uppercase for units.
- [`unit-no-unknown`](../../src/rules/unit-no-unknown/README.md): Disallow unknown units.

@@ -77,2 +77,3 @@ - [`unit-whitelist`](../../src/rules/unit-whitelist/README.md): Specify a whitelist of allowed units.

- [`value-keyword-case`](../../src/rules/value-keyword-case/README.md): Specify lowercase or uppercase for keywords values.
- [`value-list-comma-newline-after`](../../src/rules/value-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of value lists.

@@ -91,2 +92,3 @@ - [`value-list-comma-newline-before`](../../src/rules/value-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of value lists.

- [`property-blacklist`](../../src/rules/property-blacklist/README.md): Specify a blacklist of disallowed properties.
- [`property-case`](../../src/rules/property-case/README.md): Specify lowercase or uppercase for properties.
- [`property-no-vendor-prefix`](../../src/rules/property-no-vendor-prefix/README.md): Disallow vendor prefixes for properties.

@@ -147,2 +149,4 @@ - [`property-unit-blacklist`](../../src/rules/property-unit-blacklist/README.md): Specify a blacklist of disallowed units for specific properties.

- [`selector-no-vendor-prefix`](../../src/rules/selector-no-vendor-prefix/README.md): Disallow vendor prefixes for selectors.
- [`selector-pseudo-class-case`](../../src/rules/selector-pseudo-class-case/README.md): Specify lowercase or uppercase for pseudo-class selectors.
- [`selector-pseudo-element-case`](../../src/rules/selector-pseudo-element-case/README.md): Specify lowercase or uppercase for pseudo-element selectors.
- [`selector-pseudo-element-colon-notation`](../../src/rules/selector-pseudo-element-colon-notation/README.md): Specify single or double colon notation for applicable pseudo-elements.

@@ -195,2 +199,3 @@ - [`selector-root-no-composition`](../../src/rules/selector-root-no-composition/README.md): Disallow the composition of `:root` in selectors.

- [`at-rule-empty-line-before`](../../src/rules/at-rule-empty-line-before/README.md): Require or disallow an empty line before at-rules.
- [`at-rule-name-case`](../../src/rules/at-rule-name-case/README.md): Specify lowercase or uppercase for at-rules names.
- [`at-rule-no-vendor-prefix`](../../src/rules/at-rule-no-vendor-prefix/README.md): Disallow vendor prefixes for at-rules.

@@ -220,115 +225,1 @@ - [`at-rule-semicolon-newline-after`](../../src/rules/at-rule-semicolon-newline-after/README.md): Require a newline after the semicolon of at-rules.

- [`stylelint-disable-reason`](../../src/rules/stylelint-disable-reason/README.md): Require a reason comment before or after `stylelint-disable` comments.
## About rule names
- Made of lowercase words separated by hyphens.
- Split into two parts:
- The first describes what [*thing*](http://apps.workflower.fi/vocabs/css/en) the rule applies to.
- The second describes what the rule is checking.
```shell
"number-leading-zero"
↑ ↑
the thing what the rule is checking
```
- Except when the rule applies to the whole stylesheet:
```shell
"no-eol-whitespace"
"indentation"
what the rules are checking
```
### No rules
Most rules allow you to choose whether you want to require *or- disallow something.
For example, whether numbers *must* or *must not* have a leading zero:
- `number-leading-zero`: `string - "always"|"never"`
- `"always"` - there *must always* be a leading zero.
- `"never"` - there *must never* be a leading zero.
```css
a { line-height: 0.5; }
/** ↑
* This leading zero */
```
However, some rules *just disallow* something. `*-no-*` is used to identify these rules.
For example, whether empty blocks should be disallowed:
- `block-no-empty` - blocks *must not* be empty.
```css
a { }
/** ↑
* Blocks like this */
```
Notice how, for a rule like this, it does not make sense to have an option to enforce the opposite i.e. that every block *must* be empty.
### Max rules
`*-max-*` is used when a rule is *setting a limit* to something.
For example, specifying the maximum number of digits after the "." in a number:
- `number-max-precision`: `int`
```css
a { font-size: 1.333em; }
/** ↑
* The maximum number of digits after this "." */
```
### Whitespace rules
Whitespace rules allow you to specify whether an empty line, a single space, a newline or no space must be used in some specific part of the stylesheet.
The whitespace rules combine two sets of keywords:
1. `before`, `after` and `inside` are used to specify where the whitespace (if any) is expected.
2. `empty-line`, `space` and `newline` are used to specify whether a single empty line, a single space, a single newline or no space is expected there.
For example, specifying if a single empty line or no space must come before all the comments in a stylesheet:
- `comment-empty-line-before`: `string` - `"always"|"never"`
```css
a {}
/* comment */ ↑
/** ↑
* This empty line */
```
Additionally, some whitespace rule make use of another set of keywords:
1. `comma`, `colon`, `semicolon`, `opening-brace`, `closing-brace`, `opening-parenthesis`, `closing-parenthesis`, `operator` or `range-operator` are used if a specific piece of punctuation in the *thing* is being targetted.
For example, specifying if a single space or no space must come after a comma in a function:
- `function-comma-space-after`: `string` - `"always"|"never"`
```css
a { transform: translate(1, 1) }
/** ↑
* The space after this commas */
```
The plural of the punctuation is used for `inside` rules. For example, specifying if a single space or no space must be inside the parentheses of a function:
- `function-parentheses-space-inside`: `string` - `"always"|"never"`
```css
a { transform: translate( 1, 1 ); }
/** ↑ ↑
* The space inside these two parentheses */
```
{
"name": "stylelint",
"version": "5.4.0",
"version": "6.0.0",
"description": "Modern CSS linter",

@@ -33,3 +33,3 @@ "keywords": [

"autoprefixer": "^6.0.0",
"balanced-match": "^0.3.0",
"balanced-match": "^0.4.0",
"chalk": "^1.1.1",

@@ -43,4 +43,4 @@ "colorguard": "^1.1.1",

"globjoin": "^0.1.2",
"is-css-color-name": "^0.1.1",
"lodash": "^4.0.0",
"log-symbols": "^1.0.2",
"meow": "^3.3.0",

@@ -50,3 +50,3 @@ "multimatch": "^2.1.0",

"postcss": "^5.0.4",
"postcss-less": "^0.8.0",
"postcss-less": "^0.9.0",
"postcss-reporter": "^1.3.0",

@@ -60,3 +60,4 @@ "postcss-resolve-nested-selector": "^0.1.1",

"stylehacks": "^2.3.0",
"sugarss": "^0.1.2"
"sugarss": "^0.1.2",
"table": "^3.7.8"
},

@@ -113,2 +114,3 @@ "devDependencies": {

"maximum-line-length": false,
"no-heading-punctuation": false,
"no-missing-blank-lines": false,

@@ -115,0 +117,0 @@ "no-multiple-toplevel-headings": false

@@ -15,2 +15,3 @@ # stylelint

- **Completely unopinionated:** Only enable the rules you want, and configure them with options that tailor the linter to your needs.
- **Automatically fix some stylistic warnings:** By using [stylefmt](https://github.com/morishitter/stylefmt) which supports stylelint configuration files.
- **Shareable configs:** If you don't want to craft your own config, you can extend a shareable config.

@@ -28,11 +29,18 @@ - **Support for plugins:** It's easy to create your own rules and add them to the linter.

1. Install stylelint: `npm install stylelint`.
2. Choose whether you want to craft your own config or extend a pre-written, shared config.
1. Decide how you want to use stylelint:
- If you want to use a pre-written config, just find one and extend it. We recommend trying [`stylelint-config-standard`](https://github.com/stylelint/stylelint-config-standard), which includes around 60 of stylelint's rules with sensible defaults. (You can always override specific rules after extending a config.)
- To craft your own from the ground up, learn about [some rules](/docs/user-guide/rules.md) and how [they work together](/docs/user-guide/configuration.md#rules-work-together). *All of the rules are off by default*, so you only have to learn about the rules you want to turn on and enforce. That way you can start small, growing your config over time as you have a chance to explore more of the rules. Alternately, copy-paste [this example configuration](/docs/user-guide/example-config.md), which lists all of stylelint's rules and their primary options, then remove (or turn off) the rules you don't want and edit the primary option of each rule to your liking.
3. Create your [configuration](/docs/user-guide/configuration.md), probably as a `.stylelintrc` file.
4. Decide whether to use the [CLI](/docs/user-guide/cli.md), [Node API](/docs/user-guide/node-api.md), or [PostCSS plugin](/docs/user-guide/postcss-plugin.md). Be sure to [specify the syntax](/docs/user-guide/css-processors.md) if you're using non-standard code (e.g. SCSS, SugarSS or Less).
5. Lint!
- [via a plugin for your text editor](/docs/user-guide/complementary-tools.md#editor-plugins)
- [via a plugin for your build tool](/docs/user-guide/complementary-tools.md#build-tool-plugins)
- [via the stylelint CLI](/docs/user-guide/cli.md)
- [via the stylelint Node API](/docs/user-guide/node-api.md)
- [via the stylelint PostCSS plugin](/docs/user-guide/postcss-plugin.md)
2. Create your [configuration object](/docs/user-guide/configuration.md) (probably as a `.stylelintrc` file) by either crafting your own config or extending a pre-written, shared config.
- If you want to use a pre-written config, just [find one](https://www.npmjs.com/browse/keyword/stylelint-config) and [extend it](/docs/user-guide/configuration.md#extends). We recommend trying [`stylelint-config-standard`](https://github.com/stylelint/stylelint-config-standard), which includes around 60 of stylelint's rules with sensible defaults. (You can always override specific rules after extending a config.)
- To craft your own from the ground up, [learn about rules](/docs/user-guide/about-rules.md). *All of the rules are off by default*, so you only have to learn about [the rules](/docs/user-guide/rules.md) you want to turn on and enforce. That way you can start small, growing your config over time as you have a chance to explore more of the rules. Alternately, copy-paste [this example configuration](/docs/user-guide/example-config.md), which lists all of stylelint's rules and their primary options, then remove (or turn off) the rules you don't want and edit the primary option of each rule to your liking.
3. Lint!
Be sure to [specify the syntax](/docs/user-guide/css-processors.md#parsing-non-standard-syntax) if you're using non-standard syntax (e.g. SCSS, SugarSS or Less).
## Guides

@@ -42,3 +50,3 @@

- [User guide](docs/user-guide.md) - Usage, configuration and complementary tools.
- [User guide](docs/user-guide.md) - Usage, configuration, FAQ and complementary tools.
- [Developer guide](docs/developer-guide.md) - Contributing to stylelint and writing your own plugins & formatters.

@@ -48,6 +56,4 @@

There is always a lot of work to do, and already well over 100 rules to maintain. So please help out in any way that you can!
There is always a lot of work to do, and already well over 100 rules to maintain. So please help out in any way that you can:
You can help in a number of ways:
- Create, enhance, and debug rules (see our guide to ["Working on rules"](docs/developer-guide/rules.md)).

@@ -62,2 +68,6 @@ - Improve documentation.

We communicate via [issues](https://github.com/stylelint/stylelint/issues) and [pull requests](https://github.com/stylelint/stylelint/pulls).
There is also [stackoverflow](http://stackoverflow.com/questions/tagged/stylelint), which is our preferred QA forum. Tag your post with "stylelint".
## Important documents

@@ -64,0 +74,0 @@

import chalk from "chalk"
import path from "path"
import _ from "lodash"
import formatter from "postcss-reporter/lib/formatter"
import symbols from "log-symbols"
import table, { getBorderCharacters } from "table"
import utils from "postcss-reporter/lib/util"
const minimalFormatter = formatter({
noIcon: true,
noPlugin: true,
})
const MARGIN_WIDTHS = 9
export default function (results) {
let output = invalidOptionsFormatter(results)
output += deprecationsFormatter(results)
return results.reduce((output, result) => {
output += minimalFormatter({
messages: result.warnings,
source: result.source,
})
return output
}, output)
const levelColors = {
info: "blue",
warning: "yellow",
error: "red",
}

@@ -50,1 +43,100 @@

}
function logFrom(fromValue) {
if (fromValue.charAt(0) === "<") return fromValue
return path.relative(process.cwd(), fromValue).split(path.sep).join("/")
}
function getMessageWidth(columnWidths) {
if (!process.stdout.isTTY) {
return columnWidths[3]
}
const availableWidth = process.stdout.columns
const fullWidth = _.sum(_.values(columnWidths))
// If there is no reason to wrap the text, we won't align the last column to the right
if (availableWidth > fullWidth + MARGIN_WIDTHS) {
return columnWidths[3]
}
return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS)
}
function formatter(messages, source) {
if (!messages.length) return ""
const orderedMessages = _.sortBy(
messages,
(m) => m.line ? 2 : 1, // positionless first
(m) => m.line ,
(m) => m.column
)
// Create a list of column widths, needed to calculate
// the size of the message column and if needed wrap it.
const columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 }
const calculateWidths = function (columns) {
_.forOwn(columns, (value, key) => {
columnWidths[key] = Math.max(columnWidths[key], chalk.stripColor(value).toString().length)
})
return columns
}
let output = "\n"
if (source) {
output += chalk.bold.underline(logFrom(source)) + "\n"
}
const cleanedMessages = orderedMessages.map(
(message) => {
const location = utils.getLocation(message)
const severity = message.severity
const row = [
location.line || "",
location.column || "",
symbols[severity] ? chalk[levelColors[severity]](symbols[severity]) : severity,
message.text.replace(/\.$/, "").replace(new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"), ""),
chalk.yellow(message.rule || ""),
]
calculateWidths(row)
return row
})
output += table(
cleanedMessages,
{
border: getBorderCharacters("void"),
columns: {
0: { alignment: "right", width: columnWidths[0], paddingRight: 0 },
1: { alignment: "left", width: columnWidths[1] },
2: { alignment: "left", width: columnWidths[2] },
3: { alignment: "left", width: getMessageWidth(columnWidths), wrapWord: true },
4: { alignment: "left", width: columnWidths[4], paddingRight: 0 },
},
drawHorizontalLine: () => false,
}
)
.split("\n")
.map((el) => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.bold(p1 + ":" + p2)))
.join("\n")
return output
}
export default function (results) {
let output = invalidOptionsFormatter(results)
output += deprecationsFormatter(results)
return results.reduce((output, result) => {
output += formatter(result.warnings, result.source)
return output
}, output)
}

@@ -10,2 +10,3 @@ import postcssPlugin from "./postcssPlugin"

validateOptions,
cssWordIsVariable,
} from "./utils"

@@ -23,2 +24,3 @@ import {

validateOptions,
cssWordIsVariable,
}

@@ -25,0 +27,0 @@

@@ -11,3 +11,3 @@ import {

export const messages = ruleMessages(ruleName, {
expected: (h, v) => `Expected "${h}" to be "${v}"`,
expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
})

@@ -14,0 +14,0 @@

import {
cssWordIsVariable,
optionsHaveIgnored,
report,
ruleMessages,
cssWordIsVariable,
validateOptions,

@@ -14,5 +15,11 @@ } from "../../utils"

export default function (actual) {
export default function (on, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
const validOptions = validateOptions(result, ruleName, { actual: on }, {
actual: options,
possible: {
ignore: ["consecutive-duplicates"],
},
optional: true,
})
if (!validOptions) { return }

@@ -33,2 +40,3 @@

const decls = []
node.each(child => {

@@ -38,3 +46,5 @@ if (child.nodes && child.nodes.length) {

}
if (child.type !== "decl") { return }
const prop = child.prop

@@ -47,3 +57,12 @@

if (decls.indexOf(prop) !== -1) {
const indexDuplicate = decls.indexOf(prop)
if (indexDuplicate !== -1) {
if (
optionsHaveIgnored(options, "consecutive-duplicates")
&& indexDuplicate === decls.length - 1
) {
return
}
report({

@@ -56,2 +75,3 @@ message: messages.rejected(prop),

}
decls.push(prop)

@@ -58,0 +78,0 @@ })

@@ -32,1 +32,29 @@ # declaration-block-no-duplicate-properties

```
## Options
### `ignore: ["consecutive-duplicates"]`
Ignore consecutive duplicated properties.
They can prove to be useful fallbacks for older browsers.
The following patterns are considered warnings:
```css
p {
font-size: 16px;
font-weight: 400;
font-size: 1rem;
}
```
The following patterns are *not* considered warnings:
```css
p {
font-size: 16px;
font-size: 1rem;
font-weight: 400;
}
```

@@ -530,3 +530,3 @@ # declaration-block-properties-order

```js
[["all", "compose"], { unspecified: "bottomAlphabetical" }]
[["composes"], { unspecified: "bottomAlphabetical" }]
```

@@ -539,4 +539,3 @@

align-items: flex-end;
all: initial;
compose: b;
composes: b;
left: 0;

@@ -548,14 +547,4 @@ }

a {
all: initial;
align-items: flex-end;
compose: b;
composes: b;
left: 0;
}
```
```css
a {
all: initial;
compose: b;
left: 0;
align-items: flex-end;

@@ -569,4 +558,3 @@ }

a {
all: initial;
compose: b;
composes: b;
align-items: flex-end;

@@ -573,0 +561,0 @@ left: 0;

@@ -28,2 +28,3 @@ import { isNumber } from "lodash"

if (!isSingleLineString(cssStatementBlockString(rule))) { return }
if (!rule.nodes) { return }

@@ -30,0 +31,0 @@ const decls = rule.nodes.filter(node => node.type === "decl")

import {
report,
ruleMessages,
cssStatementHasEmptyBlock,
cssStatementHasBlock,
validateOptions,

@@ -26,24 +26,23 @@ } from "../../utils"

root.walkRules(rule => {
// Return early if an empty rule
if (cssStatementHasEmptyBlock(rule)) { return }
root.walkAtRules(atRule => {
if (atRule.parent === root) { return }
if (atRule !== atRule.parent.last) { return }
if (cssStatementHasBlock(atRule)) { return }
checkLastNode(atRule)
})
if (!rule.last || rule.last.type !== "decl") { return }
root.walkDecls(decl => {
if (decl !== decl.parent.last) { return }
checkLastNode(decl)
})
let errorIndexOffset = rule.toString().length
const after = rule.raw("after")
if (after) {
errorIndexOffset -= after.length
}
function checkLastNode(node) {
let message
let errorIndex
let message
if (expectation === "always") {
if (rule.raws.semicolon) { return }
errorIndex = errorIndexOffset - 1
if (node.parent.raws.semicolon) { return }
message = messages.expected
}
if (expectation === "never") {
if (!rule.raws.semicolon) { return }
errorIndex = errorIndexOffset - 2
if (!node.parent.raws.semicolon) { return }
message = messages.rejected

@@ -54,9 +53,9 @@ }

message,
node: rule,
index: errorIndex,
node,
index: node.toString().trim().length - 1,
result,
ruleName,
})
})
}
}
}

@@ -31,2 +31,6 @@ # declaration-block-trailing-semicolon

```css
a { @include foo }
```
The following patterns are *not* considered warnings:

@@ -42,2 +46,6 @@

```css
a { @include foo; }
```
### `"never"`

@@ -44,0 +52,0 @@

@@ -39,3 +39,3 @@ import { repeat, isNumber, isBoolean } from "lodash"

*/
export default function (space, options) {
export default function (space, options = {}) {
const isTab = space === "tab"

@@ -55,2 +55,4 @@ const indentChar = (isTab) ? "\t" : repeat(" ", space)

hierarchicalSelectors: [isBoolean],
indentInsideParens: [ "once", "twice", "once-at-root-twice-in-block" ],
indentClosingBrace: [isBoolean],
},

@@ -116,2 +118,4 @@ optional: true,

// otherwise there's no indentation involved.
// And check `indentClosingBrace` to see if it should be indented an extra level.
const closingBraceLevel = (options.indentClosingBrace) ? nodeLevel + 1 : nodeLevel
if (

@@ -121,6 +125,6 @@ cssStatementHasBlock(node)

&& after.indexOf("\n") !== -1
&& after.slice(after.lastIndexOf("\n") + 1) !== expectedWhitespace
&& after.slice(after.lastIndexOf("\n") + 1) !== repeat(indentChar, closingBraceLevel)
) {
report({
message: messages.expected(legibleExpectation(nodeLevel)),
message: messages.expected(legibleExpectation(closingBraceLevel)),
node,

@@ -218,3 +222,28 @@ index: node.toString().length - 1,

// Sass maps are ignored to allow for arbitrary indentation
styleSearch({ source, target: "\n", outsideParens: true }, (match) => {
styleSearch({ source, target: "\n", outsideParens: !options.indentInsideParens }, (match) => {
let expectedIndentLevel = newlineIndentLevel
// Modify for paren content and closing paren
if (options.indentInsideParens && match.insideParens) {
const isClosingParen = /^\s*\)/.test(source.slice(match.startIndex))
switch (options.indentInsideParens) {
case "once":
if (isClosingParen && !options.indentClosingBrace) { expectedIndentLevel -= 1 }
break
case "twice":
if (!isClosingParen || options.indentClosingBrace) { expectedIndentLevel += 1 }
break
case "once-at-root-twice-in-block":
if (node.parent === root) {
if (isClosingParen && !options.indentClosingBrace) {
expectedIndentLevel -= 1
}
break
}
if (!isClosingParen || options.indentClosingBrace) {
expectedIndentLevel += 1
}
break
}
}
// Starting at the index after the newline, we want to

@@ -227,7 +256,7 @@ // check that the whitespace characters (excluding newlines) before the first

if (afterNewlineSpace !== repeat(indentChar, newlineIndentLevel)) {
if (afterNewlineSpace !== repeat(indentChar, expectedIndentLevel)) {
report({
message: messages.expected(legibleExpectation(newlineIndentLevel)),
message: messages.expected(legibleExpectation(expectedIndentLevel)),
node,
index: match.startIndex + 1,
index: match.startIndex + afterNewlineSpace.length + 1,
result,

@@ -234,0 +263,0 @@ ruleName,

@@ -114,2 +114,50 @@ # indentation

### `indentInsideParens: ["once", "twice", "once-at-root-twice-in-block"]`
By default, indentation within function arguments and other parentheses are ignored. If you would like to enforce indentation inside parentheses, use this option.
`"once"` means you expect one extra indentation (of your specified type) after newlines inside parentheses, and expect the closing parenthesis to have no extra indentation. For example:
```css
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
`"twice"` means you expect two extra indentations (of your specified type) after newlines inside parentheses, and expect the closing parenthesis to have one extra indentation. For example:
```css
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
`"once-at-root-twice-in-block"` means two things: You want the behavior of `"once"`, as documented above, when the parenthetical expression is part of a node that is an immediate descendent of the root — i.e. not inside a block. And you want the behavior of `"twice"`, as documented above, when the parenthetical expression is part of a node that is inside a block. For example, with a SCSS map:
```scss
$foo: (
bar: 1,
baz: 2
);
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
### `except: ["block", "value", "param"]`

@@ -252,9 +300,11 @@

## Caveats
### `indentClosingBrace: true|false`
Function arguments are simply ignored, to allow for arbitrary indentation. So any of the following are *not* considered warnings:
If `true`, the closing brace of a block (rule or at-rule) will be expected at the same indentation level as the block's inner nodes.
For example, with `indentClosingBrace: true`, the following patterns are considered warnings:
```css
.foo {
color: rgb(0, 0, 0);
a {
color: pink;
}

@@ -264,31 +314,23 @@ ```

```css
.foo {
color: rgb(
0,
0,
0
);
@media print {
a {
color: pink;
}
}
```
And the following patterns are *not* considered warnings:
```css
.foo {
color: rgb(
0,
0,
0
);
}
a {
color: pink;
}
```
```css
.foo {
color: bar(
rgb(
0,
0,
0
)
);
}
@media print {
a {
color: pink;
}
}
```
import atRuleSemicolonNewlineAfter from "./at-rule-semicolon-newline-after"
import atRuleNameCase from "./at-rule-name-case"
import atRuleEmptyLineBefore from "./at-rule-empty-line-before"

@@ -50,2 +51,3 @@ import atRuleNoVendorPrefix from "./at-rule-no-vendor-prefix"

import functionMaxEmptyLines from "./function-max-empty-lines"
import functionNameCase from "./function-name-case"
import functionParenthesesNewlineInside from "./function-parentheses-newline-inside"

@@ -86,2 +88,3 @@ import functionParenthesesSpaceInside from "./function-parentheses-space-inside"

import propertyBlacklist from "./property-blacklist"
import propertyCase from "./property-case"
import propertyNoVendorPrefix from "./property-no-vendor-prefix"

@@ -111,2 +114,4 @@ import propertyUnitBlacklist from "./property-unit-blacklist"

import selectorNoVendorPrefix from "./selector-no-vendor-prefix"
import selectorPseudoClassCase from "./selector-pseudo-class-case"
import selectorPseudoElementCase from "./selector-pseudo-element-case"
import selectorPseudoElementColonNotation from "./selector-pseudo-element-colon-notation"

@@ -120,4 +125,6 @@ import selectorRootNoComposition from "./selector-root-no-composition"

import unitBlacklist from "./unit-blacklist"
import unitCase from "./unit-case"
import unitNoUnknown from "./unit-no-unknown"
import unitWhitelist from "./unit-whitelist"
import valueKeywordCase from "./value-keyword-case"
import valueListCommaNewlineAfter from "./value-list-comma-newline-after"

@@ -131,2 +138,3 @@ import valueListCommaNewlineBefore from "./value-list-comma-newline-before"

"at-rule-semicolon-newline-after": atRuleSemicolonNewlineAfter,
"at-rule-name-case": atRuleNameCase,
"at-rule-empty-line-before": atRuleEmptyLineBefore,

@@ -180,2 +188,3 @@ "at-rule-no-vendor-prefix": atRuleNoVendorPrefix,

"function-max-empty-lines": functionMaxEmptyLines,
"function-name-case": functionNameCase,
"function-parentheses-newline-inside": functionParenthesesNewlineInside,

@@ -216,2 +225,3 @@ "function-parentheses-space-inside": functionParenthesesSpaceInside,

"property-blacklist": propertyBlacklist,
"property-case": propertyCase,
"property-no-vendor-prefix": propertyNoVendorPrefix,

@@ -241,2 +251,4 @@ "property-unit-blacklist": propertyUnitBlacklist,

"selector-no-vendor-prefix": selectorNoVendorPrefix,
"selector-pseudo-class-case": selectorPseudoClassCase,
"selector-pseudo-element-case": selectorPseudoElementCase,
"selector-pseudo-element-colon-notation": selectorPseudoElementColonNotation,

@@ -250,4 +262,6 @@ "selector-root-no-composition": selectorRootNoComposition,

"unit-blacklist": unitBlacklist,
"unit-case": unitCase,
"unit-no-unknown": unitNoUnknown,
"unit-whitelist": unitWhitelist,
"value-keyword-case": valueKeywordCase,
"value-list-comma-newline-after": valueListCommaNewlineAfter,

@@ -254,0 +268,0 @@ "value-list-comma-newline-before": valueListCommaNewlineBefore,

@@ -6,2 +6,3 @@ import {

} from "lodash"
import valueParser from "postcss-value-parser"
import {

@@ -23,2 +24,9 @@ isKnownUnit,

const ignoredUnits = new Set([
"dpcm", "dppx", "dpi",
"kHz", "Hz",
"s", "ms",
"%",
])
export default function (actual) {

@@ -83,3 +91,6 @@ return (root, result) => {

const valueWithZero = value.slice(valueWithZeroStart, valueWithZeroEnd)
const parsedValue = valueParser.unit(valueWithZero)
if (parsedValue && parsedValue.unit && ignoredUnits.has(parsedValue.unit)) { return }
// Add the indexes to ignorableIndexes so the same value will not

@@ -98,2 +109,3 @@ // be checked multiple times.

if (isKnownUnit(valueWithZero.slice(-2))) { return 2 }
if (isKnownUnit(valueWithZero.slice(-1))) { return 1 }
return 0

@@ -100,0 +112,0 @@ }())

import { vendor } from "postcss"
import { isString } from "lodash"
import {
cssWordIsVariable,
report,

@@ -29,2 +30,4 @@ ruleMessages,

if (cssWordIsVariable(prop)) { return }
if (matchesStringOrRegExp(vendor.unprefixed(prop), blacklist)) {

@@ -31,0 +34,0 @@ report({

@@ -5,2 +5,3 @@ import { isString } from "lodash"

import {
cssWordIsVariable,
declarationValueIndexOffset,

@@ -32,6 +33,10 @@ report,

if (node.type === "function" && node.value === "url") { return false }
if (node.type !== "word") { return }
if (node.type !== "word" || cssWordIsVariable(node.value)) { return }
const unit = valueParser.unit(node.value).unit
const parsedUnit = valueParser.unit(node.value)
if (!parsedUnit) { return }
const unit = parsedUnit.unit
if (!unit || blacklist.indexOf(unit) === -1) { return }

@@ -38,0 +43,0 @@

@@ -36,5 +36,3 @@ import { isString } from "lodash"

if (node.type === "function" && node.value === "url") { return false }
if (node.type !== "word"
|| cssWordIsVariable(node.value)
) { return }
if (node.type !== "word" || cssWordIsVariable(node.value)) { return }

@@ -41,0 +39,0 @@ const parsedUnit = valueParser.unit(node.value)

@@ -5,2 +5,3 @@ import { isString } from "lodash"

import {
cssWordIsVariable,
declarationValueIndexOffset,

@@ -32,6 +33,10 @@ report,

if (node.type === "function" && node.value === "url") { return false }
if (node.type !== "word") { return }
if (node.type !== "word" || cssWordIsVariable(node.value)) { return }
const unit = valueParser.unit(node.value).unit
const parsedUnit = valueParser.unit(node.value)
if (!parsedUnit) { return }
const unit = parsedUnit.unit
if (!unit || whitelist.indexOf(unit) !== -1) { return }

@@ -38,0 +43,0 @@

@@ -103,2 +103,3 @@ import postcss from "postcss"

.then(handleResult)
.catch(cssSyntaxError)

@@ -139,3 +140,21 @@ function handleResult(postcssResult) {

}
function cssSyntaxError(error) {
if (error.name !== "CssSyntaxError") { throw error }
return {
source: error.file || "<input css 1>",
deprecations: [],
invalidOptionWarnings: [],
errored: true,
warnings: [{
line: error.line,
column: error.column,
rule: error.name,
severity: "error",
text: error.reason + " (" + error.name + ")",
}],
}
}
}
}

@@ -14,3 +14,3 @@ /**

// Absolute length units
"px", "mm", "cm", "in", "pt", "pc",
"px", "mm", "cm", "in", "pt", "pc", "q",
// Time length units

@@ -20,2 +20,6 @@ "s", "ms",

"deg", "grad", "turn", "rad",
// Frequency
"Hz", "kHz",
// Resolution
"dpi", "dpcm", "dppx",
])

@@ -22,0 +26,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc