'use strict'; const arrayEqual = require('../../utils/arrayEqual'); const eachDeclarationBlock = require('../../utils/eachDeclarationBlock'); const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const { longhandSubPropertiesOfShorthandProperties } = require('../../reference/properties'); const validateOptions = require('../../utils/validateOptions'); const vendor = require('../../utils/vendor'); const { isRegExp, isString } = require('../../utils/validateTypes'); const ruleName = 'declaration-block-no-redundant-longhand-properties'; const messages = ruleMessages(ruleName, { expected: (props) => `Expected shorthand property "${props}"`, }); const meta = { url: 'https://stylelint.io/user-guide/rules/declaration-block-no-redundant-longhand-properties', }; const IGNORED_VALUES = new Set(['inherit']); /** @type {import('stylelint').Rule} */ const rule = (primary, secondaryOptions) => { return (root, result) => { const validOptions = validateOptions( result, ruleName, { actual: primary }, { actual: secondaryOptions, possible: { ignoreShorthands: [isString, isRegExp], }, optional: true, }, ); if (!validOptions) { return; } /** @type {Map} */ const longhandToShorthands = new Map(); for (const [shorthand, longhandProps] of longhandSubPropertiesOfShorthandProperties.entries()) { if (optionsMatches(secondaryOptions, 'ignoreShorthands', shorthand)) { continue; } for (const longhand of longhandProps) { const shorthands = longhandToShorthands.get(longhand) || []; shorthands.push(shorthand); longhandToShorthands.set(longhand, shorthands); } } eachDeclarationBlock(root, (eachDecl) => { /** @type {Map} */ const longhandDeclarations = new Map(); eachDecl((decl) => { if (IGNORED_VALUES.has(decl.value)) { return; } const prop = decl.prop.toLowerCase(); const unprefixedProp = vendor.unprefixed(prop); const prefix = vendor.prefix(prop); const shorthandProperties = longhandToShorthands.get(unprefixedProp); if (!shorthandProperties) { return; } for (const shorthandProperty of shorthandProperties) { const prefixedShorthandProperty = prefix + shorthandProperty; const longhandDeclaration = longhandDeclarations.get(prefixedShorthandProperty) || []; longhandDeclaration.push(prop); longhandDeclarations.set(prefixedShorthandProperty, longhandDeclaration); const shorthandProps = /** @type {Map>} */ ( longhandSubPropertiesOfShorthandProperties ).get(shorthandProperty); const prefixedShorthandData = Array.from(shorthandProps || []).map( (item) => prefix + item, ); if (!arrayEqual(prefixedShorthandData.sort(), longhandDeclaration.sort())) { continue; } report({ ruleName, result, node: decl, word: decl.prop, message: messages.expected(prefixedShorthandProperty), }); } }); }); }; }; rule.ruleName = ruleName; rule.messages = messages; rule.meta = meta; module.exports = rule;