Socket
Socket
Sign inDemoInstall

stylelint

Package Overview
Dependencies
Maintainers
6
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 14.0.0 to 14.0.1

2

lib/augmentConfig.js

@@ -418,3 +418,3 @@ 'use strict';

if (micromatch.isMatch(filePath, filesGlobs)) {
if (micromatch.isMatch(filePath, filesGlobs, { dot: true })) {
config = mergeConfigs(config, configOverrides);

@@ -421,0 +421,0 @@ }

@@ -224,3 +224,3 @@ 'use strict';

*/
(el) => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => dim(`${p1}:${p2}`)),
(el) => el.replace(/(\d+)\s+(\d+)/, (_m, p1, p2) => dim(`${p1}:${p2}`)),
)

@@ -227,0 +227,0 @@ .join('\n');

@@ -96,6 +96,11 @@ 'use strict';

resolved = require(customSyntax);
} catch {
throw new Error(
`Cannot resolve custom syntax module "${customSyntax}". Check that module "${customSyntax}" is available and spelled correctly.`,
);
} catch (error) {
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
if (error && typeof error === 'object' && error.code === 'MODULE_NOT_FOUND') {
throw new Error(
`Cannot resolve custom syntax module "${customSyntax}". Check that module "${customSyntax}" is available and spelled correctly.`,
);
}
throw error;
}

@@ -133,3 +138,3 @@

/** @type {{ [key: string]: string }} */
const previouslyInferedExtensions = {
const previouslyInferredExtensions = {
html: 'postcss-html',

@@ -157,14 +162,8 @@ js: '@stylelint/postcss-css-in-js',

function cssSyntax(stylelint, filePath) {
const fileExtension = filePath
? path
.extname(filePath || '')
.slice(1)
.toLowerCase()
: '';
const fileExtension = filePath ? path.extname(filePath).slice(1).toLowerCase() : '';
const extensions = ['css', 'pcss', 'postcss'];
if (previouslyInferedExtensions[fileExtension]) {
if (previouslyInferredExtensions[fileExtension]) {
console.warn(
`${filePath}: When linting something other than CSS, you should install an appropriate syntax, e.g. "${previouslyInferedExtensions[fileExtension]}", and use the "customSyntax" option`,
`${filePath}: When linting something other than CSS, you should install an appropriate syntax, e.g. "${previouslyInferredExtensions[fileExtension]}", and use the "customSyntax" option`,
);

@@ -171,0 +170,0 @@ }

'use strict';
const balancedMatch = require('balanced-match');
const isWhitespace = require('../../utils/isWhitespace');
const valueParser = require('postcss-value-parser');
const declarationValueIndex = require('../../utils/declarationValueIndex');
const getDeclarationValue = require('../../utils/getDeclarationValue');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const styleSearch = require('style-search');
const setDeclarationValue = require('../../utils/setDeclarationValue');
const validateOptions = require('../../utils/validateOptions');
const valueParser = require('postcss-value-parser');

@@ -19,3 +20,4 @@ const ruleName = 'function-calc-no-unspaced-operator';

/** @typedef {{ index: number, insert: boolean }} SymbolToFix */
const OPERATORS = new Set(['*', '/', '+', '-']);
const OPERATOR_REGEX = /[*/+-]/;

@@ -27,5 +29,3 @@ /** @type {import('stylelint').Rule} */

if (!validOptions) {
return;
}
if (!validOptions) return;

@@ -42,159 +42,259 @@ /**

root.walkDecls((decl) => {
/** @type {SymbolToFix[]} */
const symbolsToFix = [];
let needsFix = false;
const valueIndex = declarationValueIndex(decl);
const parsedValue = valueParser(getDeclarationValue(decl));
valueParser(decl.value).walk((node) => {
if (node.type !== 'function' || node.value.toLowerCase() !== 'calc') {
return;
/**
* @param {import('postcss-value-parser').Node[]} nodes
* @param {number} operatorIndex
* @param {-1 | 1} direction
*/
function checkAroundOperator(nodes, operatorIndex, direction) {
const isBeforeOp = direction === -1;
const currentNode = nodes[operatorIndex + direction];
const operator = nodes[operatorIndex].value;
const operatorSourceIndex = nodes[operatorIndex].sourceIndex;
if (currentNode && !isSingleSpace(currentNode)) {
if (currentNode.type === 'word') {
if (isBeforeOp) {
const lastChar = currentNode.value.slice(-1);
if (OPERATORS.has(lastChar)) {
if (context.fix) {
currentNode.value = `${currentNode.value.slice(0, -1)} ${lastChar}`;
return true;
}
complain(messages.expectedOperatorBeforeSign(operator), decl, operatorSourceIndex);
return true;
}
} else {
const firstChar = currentNode.value.slice(0, 1);
if (OPERATORS.has(firstChar)) {
if (context.fix) {
currentNode.value = `${firstChar} ${currentNode.value.slice(1)}`;
return true;
}
complain(messages.expectedAfter(operator), decl, operatorSourceIndex);
return true;
}
}
if (context.fix) {
needsFix = true;
currentNode.value = isBeforeOp ? `${currentNode.value} ` : ` ${currentNode.value}`;
return true;
}
complain(
isBeforeOp ? messages.expectedBefore(operator) : messages.expectedAfter(operator),
decl,
valueIndex + operatorSourceIndex,
);
return true;
}
if (currentNode.type === 'space') {
const indexOfFirstNewLine = currentNode.value.search(/(\n|\r\n)/);
if (indexOfFirstNewLine === 0) return;
if (context.fix) {
needsFix = true;
currentNode.value =
indexOfFirstNewLine === -1 ? ' ' : currentNode.value.slice(indexOfFirstNewLine);
return true;
}
const message = isBeforeOp
? messages.expectedBefore(operator)
: messages.expectedAfter(operator);
complain(message, decl, valueIndex + operatorSourceIndex);
return true;
}
if (currentNode.type === 'function') {
if (context.fix) {
needsFix = true;
nodes.splice(operatorIndex, 0, { type: 'space', value: ' ', sourceIndex: 0 });
return true;
}
const message = isBeforeOp
? messages.expectedBefore(operator)
: messages.expectedAfter(operator);
complain(message, decl, valueIndex + operatorSourceIndex);
return true;
}
}
const nodeText = valueParser.stringify(node);
const parensMatch = balancedMatch('(', ')', nodeText);
return false;
}
if (!parensMatch) {
throw new Error(`No parens match: "${nodeText}"`);
/**
* @param {import('postcss-value-parser').Node[]} nodes
*/
function checkForOperatorInFirstNode(nodes) {
const firstNode = nodes[0];
const operatorIndex =
(firstNode.type === 'word' || -1) && firstNode.value.search(OPERATOR_REGEX);
const operator = firstNode.value.slice(operatorIndex, operatorIndex + 1);
if (operatorIndex <= 0) return false;
const charBefore = firstNode.value.charAt(operatorIndex - 1);
const charAfter = firstNode.value.charAt(operatorIndex + 1);
if (charBefore && charBefore !== ' ' && charAfter && charAfter !== ' ') {
if (context.fix) {
needsFix = true;
firstNode.value = insertCharAtIndex(firstNode.value, operatorIndex + 1, ' ');
firstNode.value = insertCharAtIndex(firstNode.value, operatorIndex, ' ');
} else {
complain(
messages.expectedBefore(operator),
decl,
valueIndex + firstNode.sourceIndex + operatorIndex,
);
complain(
messages.expectedAfter(operator),
decl,
valueIndex + firstNode.sourceIndex + operatorIndex + 1,
);
}
} else if (charBefore && charBefore !== ' ') {
if (context.fix) {
needsFix = true;
firstNode.value = insertCharAtIndex(firstNode.value, operatorIndex, ' ');
} else {
complain(
messages.expectedBefore(operator),
decl,
valueIndex + firstNode.sourceIndex + operatorIndex,
);
}
} else if (charAfter && charAfter !== ' ') {
if (context.fix) {
needsFix = true;
firstNode.value = insertCharAtIndex(firstNode.value, operatorIndex, ' ');
} else {
complain(
messages.expectedAfter(operator),
decl,
valueIndex + firstNode.sourceIndex + operatorIndex + 1,
);
}
}
if (decl.source == null || decl.source.start == null) {
throw new Error('Declaration source must be present');
return true;
}
/**
* @param {import('postcss-value-parser').Node[]} nodes
*/
function checkForOperatorInLastNode(nodes) {
if (nodes.length === 1) return false;
const lastNode = nodes[nodes.length - 1];
const operatorIndex =
(lastNode.type === 'word' || -1) && lastNode.value.search(OPERATOR_REGEX);
if (lastNode.value[operatorIndex - 1] === ' ') return false;
if (context.fix) {
needsFix = true;
lastNode.value = insertCharAtIndex(lastNode.value, operatorIndex + 1, ' ').trim();
lastNode.value = insertCharAtIndex(lastNode.value, operatorIndex, ' ').trim();
return true;
}
const rawExpression = parensMatch.body;
const expressionIndex =
decl.source.start.column +
decl.prop.length +
(decl.raws.between || '').length +
node.sourceIndex;
const expression = blurVariables(rawExpression);
complain(
messages.expectedOperatorBeforeSign(lastNode.value[operatorIndex]),
decl,
valueIndex + lastNode.sourceIndex + operatorIndex,
);
const parensMatchStart = parensMatch.start;
return true;
}
checkSymbol('+');
checkSymbol('-');
checkSymbol('*');
checkSymbol('/');
/**
* @param {import('postcss-value-parser').Node[]} nodes
*/
function checkWords(nodes) {
if (checkForOperatorInFirstNode(nodes)) return;
/**
* @param {string} symbol
*/
function checkSymbol(symbol) {
/** @type {import('style-search').Options} */
const styleSearchOptions = {
source: expression,
target: symbol,
functionArguments: 'skip',
};
if (checkForOperatorInLastNode(nodes)) return;
styleSearch(styleSearchOptions, (match) => {
const index = match.startIndex;
const symbolIndex = node.sourceIndex + parensMatchStart + index + 1;
nodes.forEach((node, index) => {
const lastChar = node.value.slice(-1);
const firstChar = node.value.slice(0, 1);
// Deal with signs.
// (@ and $ are considered "digits" here to allow for variable syntaxes
// that permit signs in front of variables, e.g. `-$number`)
// As is "." to deal with fractional numbers without a leading zero
if ((symbol === '+' || symbol === '-') && /[\d@$.]/.test(expression[index + 1])) {
const expressionBeforeSign = expression.slice(0, index);
if (node.type === 'word') {
if (index === 0 && OPERATORS.has(lastChar)) {
if (context.fix) {
node.value = `${node.value.slice(0, -1)} ${lastChar}`;
// Ignore signs that directly follow a opening bracket
if (expressionBeforeSign[expressionBeforeSign.length - 1] === '(') {
return;
}
// Ignore signs at the beginning of the expression
if (/^\s*$/.test(expressionBeforeSign)) {
return;
}
complain(messages.expectedBefore(lastChar), decl, node.sourceIndex);
} else if (index === nodes.length && OPERATORS.has(firstChar)) {
if (context.fix) {
node.value = `${firstChar} ${node.value.slice(1)}`;
// Otherwise, ensure that there is a real operator preceding them
if (/[*/+-]\s*$/.test(expressionBeforeSign)) {
return;
}
if (!context.fix) {
// And if not, complain
complain(
messages.expectedOperatorBeforeSign(symbol),
decl,
expressionIndex + index,
);
return;
}
complain(messages.expectedOperatorBeforeSign(firstChar), decl, node.sourceIndex);
}
}
});
}
const beforeOk =
(expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) ||
newlineBefore(expression, index - 1);
parsedValue.walk((node) => {
if (node.type !== 'function' || node.value.toLowerCase() !== 'calc') return;
if (!beforeOk) {
if (context.fix) {
let step = 1;
let foundOperatorNode = false;
// Remove all whitespace characters before the operator, e.g. `\t`
while (isWhitespace(expression[index - step])) {
symbolsToFix.push({
index: symbolIndex - step,
insert: false,
});
for (const [nodeIndex, currNode] of node.nodes.entries()) {
if (currNode.type !== 'word' || !OPERATORS.has(currNode.value)) continue;
step++;
}
foundOperatorNode = true;
// Add only one space character
symbolsToFix.push({
index: symbolIndex,
insert: true,
});
} else {
complain(messages.expectedBefore(symbol), decl, expressionIndex + index);
}
}
const nodeBefore = node.nodes[nodeIndex - 1];
const nodeAfter = node.nodes[nodeIndex + 1];
const afterOk =
(expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) ||
isNewlineAtIndex(expression, index + 1);
if (isSingleSpace(nodeBefore) && isSingleSpace(nodeAfter)) continue;
if (!afterOk) {
if (context.fix) {
let step = 1;
let spaceNeeded = true;
if (checkAroundOperator(node.nodes, nodeIndex, 1)) continue;
// Remove all whitespace characters before the operator or \n, e.g. \t
while (isWhitespace(expression[index + step])) {
if (isNewlineAtIndex(expression, index + step)) {
spaceNeeded = false;
break;
}
checkAroundOperator(node.nodes, nodeIndex, -1);
}
symbolsToFix.push({
index: symbolIndex + step,
insert: false,
});
step++;
}
// Insert one space character if there is no \n
if (spaceNeeded) {
symbolsToFix.push({
index: symbolIndex + 1,
insert: true,
});
}
} else {
complain(messages.expectedAfter(symbol), decl, expressionIndex + index);
}
}
});
if (!foundOperatorNode) {
checkWords(node.nodes);
}
});
if (context.fix) {
decl.value = symbolsToFix.reduce((/** @type {string} */ fixedValue, { insert, index }) => {
shiftIndexes(symbolsToFix, index, insert);
return insert
? insertCharAtIndex(fixedValue, index, ' ')
: removeCharAtIndex(fixedValue, index);
}, decl.value);
if (needsFix) {
setDeclarationValue(decl, parsedValue.toString());
}

@@ -208,31 +308,2 @@ });

* @param {number} index
*/
function isNewlineAtIndex(str, index) {
return str[index] === '\n' || str.slice(index, index + 2) === '\r\n';
}
/**
* @param {SymbolToFix[]} symbolsToFix
* @param {number} index
* @param {boolean} insert
*/
function shiftIndexes(symbolsToFix, index, insert) {
symbolsToFix.forEach((symbol) => {
if (symbol.index > index) {
symbol.index += insert ? 1 : -1;
}
});
}
/**
* @param {string} str
* @param {number} index
*/
function removeCharAtIndex(str, index) {
return str.slice(0, index) + str.slice(index + 1, str.length);
}
/**
* @param {string} str
* @param {number} index
* @param {string} char

@@ -245,26 +316,11 @@ */

/**
* @param {string} source
* @param {import('postcss-value-parser').Node} node
* @returns {node is import('postcss-value-parser').SpaceNode & { value: ' ' } }
*/
function blurVariables(source) {
return source.replace(/[$@][^)\s]+|#\{.+?\}/g, '0');
function isSingleSpace(node) {
return node && node.type === 'space' && node.value === ' ';
}
/**
* @param {string} str
* @param {number} startIndex
*/
function newlineBefore(str, startIndex) {
let index = startIndex;
while (index && isWhitespace(str[index])) {
if (str[index] === '\n') return true;
index--;
}
return false;
}
rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;

@@ -9,5 +9,5 @@ 'use strict';

function isRectangular(areas) {
return areas.every((row, i, arr) => row.length === arr[0].length);
return areas.every((row, _i, arr) => row.length === arr[0].length);
}
module.exports = isRectangular;
{
"name": "stylelint",
"version": "14.0.0",
"version": "14.0.1",
"description": "A mighty, modern CSS linter.",

@@ -26,15 +26,11 @@ "keywords": [

"main": "lib/index.js",
"types": "types/stylelint/index.d.ts",
"bin": {
"stylelint": "bin/stylelint.js"
},
"types": "types/stylelint/index.d.ts",
"files": [
"bin",
"CHANGELOG.md",
"CONTRIBUTING.md",
"SECURITY.md",
"docs",
"lib",
"!**/__tests__",
"!lib/testUtils",
"bin/**/*.js",
"lib/**/*.js",
"!**/__tests__/**",
"!lib/testUtils/**",
"types/stylelint/index.d.ts"

@@ -125,3 +121,3 @@ ],

"global-modules": "^2.0.0",
"globby": "^11.0.3",
"globby": "^11.0.4",
"globjoin": "^0.1.4",

@@ -148,4 +144,4 @@ "html-tags": "^3.1.0",

"specificity": "^0.4.1",
"string-width": "^4.2.2",
"strip-ansi": "^6.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"style-search": "^0.1.0",

@@ -166,3 +162,2 @@ "svg-tags": "^1.0.0",

"@types/imurmurhash": "^0.1.1",
"@types/jest": "^27.0.2",
"@types/micromatch": "^4.0.2",

@@ -185,3 +180,3 @@ "@types/normalize-path": "^3.0.0",

"jest-watch-typeahead": "^1.0.0",
"lint-staged": "^11.2.3",
"lint-staged": "^11.2.4",
"np": "^7.5.0",

@@ -188,0 +183,0 @@ "npm-run-all": "^4.1.5",

@@ -27,3 +27,3 @@ # Stylelint

![Example](https://github.com/stylelint/stylelint/raw/master/example.png?raw=true)
![Example](https://github.com/stylelint/stylelint/raw/main/example.png?raw=true)

@@ -95,2 +95,2 @@ ## Guides

[The MIT License](https://raw.githubusercontent.com/stylelint/stylelint/master/LICENSE).
[The MIT License](https://raw.githubusercontent.com/stylelint/stylelint/main/LICENSE).
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