diff --git a/.changeset/smart-lobsters-roll.md b/.changeset/smart-lobsters-roll.md new file mode 100644 index 0000000000..bd6f2812d0 --- /dev/null +++ b/.changeset/smart-lobsters-roll.md @@ -0,0 +1,5 @@ +--- +"stylelint": patch +--- + +Added: `ignore: ["custom-elements"]` to `selector-max-type` diff --git a/lib/rules/selector-max-type/README.md b/lib/rules/selector-max-type/README.md index ab890d00a8..610b554e4a 100644 --- a/lib/rules/selector-max-type/README.md +++ b/lib/rules/selector-max-type/README.md @@ -77,7 +77,7 @@ div a .foo:not(span) {} ## Optional secondary options -### `ignore: ["child", "compounded", "descendant", "next-sibling"]` +### `ignore: ["child", "compounded", "custom-elements", "descendant", "next-sibling"]` #### `"child"` @@ -115,6 +115,19 @@ div span a.foo {} div span a#bar {} ``` +#### `"custom-elements"` + +Discount custom elements. + +For example, with `2`: + +The following pattern is _not_ considered a problem: + + +```css +div a foo-bar {} +``` + #### `"descendant"` Discount descendant type selectors. diff --git a/lib/rules/selector-max-type/__tests__/index.js b/lib/rules/selector-max-type/__tests__/index.js index 7177a7bbc2..6d13b85ca4 100644 --- a/lib/rules/selector-max-type/__tests__/index.js +++ b/lib/rules/selector-max-type/__tests__/index.js @@ -489,6 +489,43 @@ testRule({ ], }); +testRule({ + ruleName, + config: [0, { ignore: ['custom-elements'] }], + + accept: [ + { + code: 'x-foo {}', + description: 'custom element', + }, + { + code: '.foo x-foo {}', + description: 'class and custom element', + }, + { + code: 'custom-element::part(foo) {}', + description: 'shadow parts', + }, + ], + + reject: [ + { + code: 'x-Foo {}', + description: 'invalid custom element', + message: messages.expected('x-Foo', 0), + line: 1, + column: 1, + }, + { + code: 'x-foo div {}', + description: 'custom element and type', + message: messages.expected('x-foo div', 0), + line: 1, + column: 1, + }, + ], +}); + testRule({ ruleName, config: [0, { ignore: ['compounded', 'descendant'] }], diff --git a/lib/rules/selector-max-type/index.js b/lib/rules/selector-max-type/index.js index b48ff5fe22..e9c331c562 100644 --- a/lib/rules/selector-max-type/index.js +++ b/lib/rules/selector-max-type/index.js @@ -14,6 +14,7 @@ const resolvedNestedSelector = require('postcss-resolve-nested-selector'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); const { isRegExp, isString } = require('../../utils/validateTypes'); +const isCustomElement = require('../../utils/isCustomElement'); const ruleName = 'selector-max-type'; @@ -41,7 +42,7 @@ const rule = (primary, secondaryOptions) => { { actual: secondaryOptions, possible: { - ignore: ['descendant', 'child', 'compounded', 'next-sibling'], + ignore: ['descendant', 'child', 'compounded', 'next-sibling', 'custom-elements'], ignoreTypes: [isString, isRegExp], }, optional: true, @@ -56,6 +57,7 @@ const rule = (primary, secondaryOptions) => { const ignoreChild = optionsMatches(secondaryOptions, 'ignore', 'child'); const ignoreCompounded = optionsMatches(secondaryOptions, 'ignore', 'compounded'); const ignoreNextSibling = optionsMatches(secondaryOptions, 'ignore', 'next-sibling'); + const ignoreCustomElements = optionsMatches(secondaryOptions, 'ignore', 'custom-elements'); /** * @param {import('postcss-selector-parser').Container} selectorNode @@ -88,6 +90,10 @@ const rule = (primary, secondaryOptions) => { return total; } + if (ignoreCustomElements && childNode.value && isCustomElement(childNode.value)) { + return total; + } + if (childNode.type === 'tag' && !isStandardSyntaxTypeSelector(childNode)) { return total; }