Skip to content

Commit

Permalink
Merge branch 'master' into browser-bundle
Browse files Browse the repository at this point in the history
* master:
  Update CHANGELOG.md
  Fix false negatives for where, is, nth-child and nth-last-child in selector-max-* (except selector-max-type) (#4842)
  Bump @types/lodash from 4.14.155 to 4.14.157 (#4869)
  Remove `postcss-reporter` package (#4858)
  Bump lodash from 4.17.15 to 4.17.19 (#4864)
  Replace 3rd-party type definitions (#4857)
  • Loading branch information
m-allanson committed Jul 23, 2020
2 parents 32a9a8b + ac0519d commit 56ff536
Show file tree
Hide file tree
Showing 31 changed files with 270 additions and 244 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@ All notable changes to this project are documented in this file.
- Added: `*-allowed-list`, `*-disallowed-list` and `*-required-list` new names for `*-whitelist`, `*-blacklist` and `*-requirelist` rules, respectively; the rules are aliased as their old names ([#4845](https://github.com/stylelint/stylelint/pull/4845)).
- Added: `ignoreContextFunctionalPseudoClasses` to `selector-max-id` ([#4835](https://github.com/stylelint/stylelint/pull/4835)).
- Added: `ignoreComments[]` to `comment-empty-line-before` ([#4841](https://github.com/stylelint/stylelint/pull/4841)).
- Fixed: false negatives for `where`, `is`, `nth-child` and `nth-last-child` in `selector-max-*` rules (except selector-max-type) ([#4842](https://github.com/stylelint/stylelint/pull/4842)).

## 13.6.1

Expand Down
2 changes: 1 addition & 1 deletion lib/formatters/__tests__/verboseFormatter.test.js
Expand Up @@ -4,7 +4,7 @@ const prepareFormatterOutput = require('./prepareFormatterOutput');
const stripIndent = require('common-tags').stripIndent;
const verboseFormatter = require('../verboseFormatter');

describe('stringFormatter', () => {
describe('verboseFormatter', () => {
it('outputs no warnings', () => {
const results = [
{
Expand Down
7 changes: 3 additions & 4 deletions lib/formatters/stringFormatter.js
Expand Up @@ -6,7 +6,6 @@ const path = require('path');
const stringWidth = require('string-width');
const symbols = require('log-symbols');
const table = require('table');
const utils = require('postcss-reporter/lib/util');

const MARGIN_WIDTHS = 9;

Expand Down Expand Up @@ -134,14 +133,14 @@ function formatter(messages, source) {
}

const cleanedMessages = orderedMessages.map((message) => {
const location = utils.getLocation(message);
const { line, column } = message;
const severity = /** @type {keyof import('log-symbols')} */ (message.severity);
/**
* @type {[string, string, string, string, string]}
*/
const row = [
location.line ? location.line.toString() : '',
location.column ? location.column.toString() : '',
line ? line.toString() : '',
column ? column.toString() : '',
symbols[severity]
? chalk[/** @type {'blue' | 'red' | 'yellow'} */ (levelColors[severity])](symbols[severity])
: severity,
Expand Down
12 changes: 6 additions & 6 deletions lib/reference/keywordSets.js
Expand Up @@ -208,9 +208,7 @@ keywordSets.pseudoElements = uniteSets(
);

keywordSets.aNPlusBNotationPseudoClasses = new Set([
'nth-child',
'nth-column',
'nth-last-child',
'nth-last-column',
'nth-last-of-type',
'nth-of-type',
Expand All @@ -220,6 +218,10 @@ keywordSets.linguisticPseudoClasses = new Set(['dir', 'lang']);

keywordSets.atRulePagePseudoClasses = new Set(['first', 'right', 'left', 'blank']);

keywordSets.logicalCombinationsPseudoClasses = new Set(['has', 'is', 'matches', 'not', 'where']);

keywordSets.aNPlusBOfSNotationPseudoClasses = new Set(['nth-child', 'nth-last-child']);

keywordSets.otherPseudoClasses = new Set([
'active',
'any-link',
Expand All @@ -241,19 +243,15 @@ keywordSets.otherPseudoClasses = new Set([
'focus-visible',
'fullscreen',
'future',
'has',
'host',
'host-context',
'hover',
'indeterminate',
'in-range',
'invalid',
'is',
'last-child',
'last-of-type',
'link',
'matches',
'not',
'only-child',
'only-of-type',
'optional',
Expand Down Expand Up @@ -302,6 +300,8 @@ keywordSets.webkitProprietaryPseudoClasses = new Set([
keywordSets.pseudoClasses = uniteSets(
keywordSets.aNPlusBNotationPseudoClasses,
keywordSets.linguisticPseudoClasses,
keywordSets.logicalCombinationsPseudoClasses,
keywordSets.aNPlusBOfSNotationPseudoClasses,
keywordSets.otherPseudoClasses,
);

Expand Down
8 changes: 7 additions & 1 deletion lib/rules/function-calc-no-unspaced-operator/index.js
Expand Up @@ -36,7 +36,13 @@ function rule(actual) {
return;
}

const parensMatch = balancedMatch('(', ')', valueParser.stringify(node));
const nodeText = valueParser.stringify(node);
const parensMatch = balancedMatch('(', ')', nodeText);

if (!parensMatch) {
throw new Error(`No parens match: "${nodeText}"`);
}

const rawExpression = parensMatch.body;
const expressionIndex =
decl.source.start.column +
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/selector-max-attribute/index.js
Expand Up @@ -3,7 +3,7 @@
'use strict';

const _ = require('lodash');
const isLogicalCombination = require('../../utils/isLogicalCombination');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const optionsMatches = require('../../utils/optionsMatches');
const parseSelector = require('../../utils/parseSelector');
Expand Down Expand Up @@ -49,8 +49,8 @@ function rule(max, options) {

function checkSelector(selectorNode, ruleNode) {
const count = selectorNode.reduce((total, childNode) => {
// Only traverse inside actual selectors and logical combinations
if (childNode.type === 'selector' || isLogicalCombination(childNode)) {
// Only traverse inside actual selectors and context functional pseudo-classes
if (childNode.type === 'selector' || isContextFunctionalPseudoClass(childNode)) {
checkSelector(childNode, ruleNode);
}

Expand Down
6 changes: 3 additions & 3 deletions lib/rules/selector-max-class/index.js
Expand Up @@ -2,7 +2,7 @@

'use strict';

const isLogicalCombination = require('../../utils/isLogicalCombination');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const parseSelector = require('../../utils/parseSelector');
const report = require('../../utils/report');
Expand Down Expand Up @@ -34,8 +34,8 @@ function rule(max) {

function checkSelector(selectorNode, ruleNode) {
const count = selectorNode.reduce((total, childNode) => {
// Only traverse inside actual selectors and logical combinations
if (childNode.type === 'selector' || isLogicalCombination(childNode)) {
// Only traverse inside actual selectors and context functional pseudo-classes
if (childNode.type === 'selector' || isContextFunctionalPseudoClass(childNode)) {
checkSelector(childNode, ruleNode);
}

Expand Down
6 changes: 3 additions & 3 deletions lib/rules/selector-max-compound-selectors/index.js
Expand Up @@ -2,7 +2,7 @@

'use strict';

const isLogicalCombination = require('../../utils/isLogicalCombination');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const parseSelector = require('../../utils/parseSelector');
const report = require('../../utils/report');
Expand Down Expand Up @@ -39,8 +39,8 @@ function rule(max) {
let compoundCount = 1;

selectorNode.each((childNode) => {
// Only traverse inside actual selectors and logical combinations
if (childNode.type === 'selector' || isLogicalCombination(childNode)) {
// Only traverse inside actual selectors and context functional pseudo-classes
if (childNode.type === 'selector' || isContextFunctionalPseudoClass(childNode)) {
checkSelector(childNode, rule);
}

Expand Down
6 changes: 3 additions & 3 deletions lib/rules/selector-max-id/index.js
Expand Up @@ -3,7 +3,7 @@
'use strict';

const _ = require('lodash');
const isLogicalCombination = require('../../utils/isLogicalCombination');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const optionsMatches = require('../../utils/optionsMatches');
const parseSelector = require('../../utils/parseSelector');
Expand Down Expand Up @@ -47,10 +47,10 @@ function rule(max, options) {

function checkSelector(selectorNode, ruleNode) {
const count = selectorNode.reduce((total, childNode) => {
// Only traverse inside actual selectors and logical combinations that are not part of ignored functional pseudo-classes
// Only traverse inside actual selectors and context functional pseudo-classes that are not part of ignored functional pseudo-classes
if (
childNode.type === 'selector' ||
(isLogicalCombination(childNode) &&
(isContextFunctionalPseudoClass(childNode) &&
!isIgnoredContextFunctionalPseudoClass(childNode, options))
) {
checkSelector(childNode, ruleNode);
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/selector-max-pseudo-class/index.js
Expand Up @@ -2,7 +2,7 @@

'use strict';

const isLogicalCombination = require('../../utils/isLogicalCombination');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const keywordSets = require('../../reference/keywordSets');
const parseSelector = require('../../utils/parseSelector');
Expand Down Expand Up @@ -35,8 +35,8 @@ function rule(max) {

function checkSelector(selectorNode, ruleNode) {
const count = selectorNode.reduce((total, childNode) => {
// Only traverse inside actual selectors and logical combinations
if (childNode.type === 'selector' || isLogicalCombination(childNode)) {
// Only traverse inside actual selectors and context functional pseudo-classes
if (childNode.type === 'selector' || isContextFunctionalPseudoClass(childNode)) {
checkSelector(childNode, ruleNode);
}

Expand Down
11 changes: 8 additions & 3 deletions lib/rules/selector-max-type/index.js
Expand Up @@ -3,11 +3,12 @@
'use strict';

const _ = require('lodash');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
const isKeyframeSelector = require('../../utils/isKeyframeSelector');
const isLogicalCombination = require('../../utils/isLogicalCombination');
const isOnlyWhitespace = require('../../utils/isOnlyWhitespace');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const isStandardSyntaxSelector = require('../../utils/isStandardSyntaxSelector');
const isStandardSyntaxTypeSelector = require('../../utils/isStandardSyntaxTypeSelector');
const optionsMatches = require('../../utils/optionsMatches');
const parseSelector = require('../../utils/parseSelector');
const report = require('../../utils/report');
Expand Down Expand Up @@ -56,8 +57,8 @@ function rule(max, options) {

function checkSelector(selectorNode, ruleNode) {
const count = selectorNode.reduce((total, childNode) => {
// Only traverse inside actual selectors and logical combinations
if (childNode.type === 'selector' || isLogicalCombination(childNode)) {
// Only traverse inside actual selectors and context functional pseudo-classes
if (childNode.type === 'selector' || isContextFunctionalPseudoClass(childNode)) {
checkSelector(childNode, ruleNode);
}

Expand All @@ -81,6 +82,10 @@ function rule(max, options) {
return total;
}

if (childNode.type === 'tag' && !isStandardSyntaxTypeSelector(childNode)) {
return total;
}

return total + (childNode.type === 'tag');
}, 0);

Expand Down
3 changes: 3 additions & 0 deletions lib/utils/FileCache.js
Expand Up @@ -8,6 +8,8 @@ const path = require('path');
const DEFAULT_CACHE_LOCATION = './.stylelintcache';
const DEFAULT_HASH = '';

/** @typedef {import('file-entry-cache').FileDescriptor["meta"] & { hashOfConfig?: string }} CacheMetadata */

/**
* @param {string} [cacheLocation]
* @param {string} [hashOfConfig]
Expand All @@ -30,6 +32,7 @@ class FileCache {
// Get file descriptor compares current metadata against cached
// one and stores the result to "changed" prop.w
const descriptor = this._fileCache.getFileDescriptor(absoluteFilepath);
/** @type {CacheMetadata} */
const meta = descriptor.meta || {};
const changed = descriptor.changed || meta.hashOfConfig !== this._hashOfConfig;

Expand Down
48 changes: 48 additions & 0 deletions lib/utils/__tests__/isContextFunctionalPseudoClass.test.js
@@ -0,0 +1,48 @@
'use strict';

const isContextFunctionalPseudoClass = require('../isContextFunctionalPseudoClass');
const parseSelector = require('postcss-selector-parser');
const postcss = require('postcss');

function selector(css, cb) {
postcss.parse(css).walkRules((rule) => {
parseSelector((selectorAST) => {
selectorAST.walkPseudos(cb);
}).processSync(rule.selector);
});
}

describe('isContextFunctionalPseudoClass', () => {
it('handles non-context-functional pseudo-classes, like hover', () => {
selector('a:hover {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(false);
});
});

it('handles logical combinations, ', () => {
selector('a:has(.foo) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
selector('a:is(.foo) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
selector('a:matches(.foo) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
selector('a:not(.foo) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
selector('a:where(.foo) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
});

it('handles tree structural/NPlusBOfSNotationPseudoClasses classes', () => {
selector('a:nth-child(n+7) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
selector('a:nth-last-child(n+7) {}', (selector) => {
expect(isContextFunctionalPseudoClass(selector)).toBe(true);
});
});
});
39 changes: 0 additions & 39 deletions lib/utils/__tests__/isLogicalCombination.test.js

This file was deleted.

9 changes: 7 additions & 2 deletions lib/utils/blurFunctionArguments.js
Expand Up @@ -33,8 +33,13 @@ module.exports = function (source, functionName, blurChar = '`') {
while (lowerCaseSource.includes(nameWithParen, searchStartIndex)) {
const openingParenIndex =
lowerCaseSource.indexOf(nameWithParen, searchStartIndex) + functionNameLength;
const closingParenIndex =
balancedMatch('(', ')', lowerCaseSource.slice(openingParenIndex)).end + openingParenIndex;
const parensMatch = balancedMatch('(', ')', lowerCaseSource.slice(openingParenIndex));

if (!parensMatch) {
throw new Error(`No parens match: "${source}"`);
}

const closingParenIndex = parensMatch.end + openingParenIndex;
const argumentsLength = closingParenIndex - openingParenIndex - 1;

result =
Expand Down
4 changes: 4 additions & 0 deletions lib/utils/functionArgumentsSearch.js
Expand Up @@ -31,6 +31,10 @@ module.exports = function (source, functionName, callback) {

const parensMatch = balancedMatch('(', ')', source.substr(match.startIndex));

if (!parensMatch) {
throw new Error(`No parens match: "${source}"`);
}

callback(parensMatch.body, match.endIndex + 1);
},
);
Expand Down

0 comments on commit 56ff536

Please sign in to comment.