| 'use strict'; |
| |
| const declarationValueIndex = require('../../utils/declarationValueIndex'); |
| const getDeclarationValue = require('../../utils/getDeclarationValue'); |
| const isSingleLineString = require('../../utils/isSingleLineString'); |
| const isStandardSyntaxFunction = require('../../utils/isStandardSyntaxFunction'); |
| const report = require('../../utils/report'); |
| const ruleMessages = require('../../utils/ruleMessages'); |
| const setDeclarationValue = require('../../utils/setDeclarationValue'); |
| const validateOptions = require('../../utils/validateOptions'); |
| const valueParser = require('postcss-value-parser'); |
| |
| const ruleName = 'function-parentheses-space-inside'; |
| |
| const messages = ruleMessages(ruleName, { |
| expectedOpening: 'Expected single space after "("', |
| rejectedOpening: 'Unexpected whitespace after "("', |
| expectedClosing: 'Expected single space before ")"', |
| rejectedClosing: 'Unexpected whitespace before ")"', |
| expectedOpeningSingleLine: 'Expected single space after "(" in a single-line function', |
| rejectedOpeningSingleLine: 'Unexpected whitespace after "(" in a single-line function', |
| expectedClosingSingleLine: 'Expected single space before ")" in a single-line function', |
| rejectedClosingSingleLine: 'Unexpected whitespace before ")" in a single-line function', |
| }); |
| |
| /** @type {import('stylelint').Rule} */ |
| const rule = (primary, _secondaryOptions, context) => { |
| return (root, result) => { |
| const validOptions = validateOptions(result, ruleName, { |
| actual: primary, |
| possible: ['always', 'never', 'always-single-line', 'never-single-line'], |
| }); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| root.walkDecls((decl) => { |
| if (!decl.value.includes('(')) { |
| return; |
| } |
| |
| let hasFixed = false; |
| const declValue = getDeclarationValue(decl); |
| const parsedValue = valueParser(declValue); |
| |
| parsedValue.walk((valueNode) => { |
| if (valueNode.type !== 'function') { |
| return; |
| } |
| |
| if (!isStandardSyntaxFunction(valueNode)) { |
| return; |
| } |
| |
| // Ignore function without parameters |
| if (!valueNode.nodes.length) { |
| return; |
| } |
| |
| const functionString = valueParser.stringify(valueNode); |
| const isSingleLine = isSingleLineString(functionString); |
| |
| // Check opening ... |
| |
| const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1; |
| |
| if (primary === 'always' && valueNode.before !== ' ') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.before = ' '; |
| } else { |
| complain(messages.expectedOpening, openingIndex); |
| } |
| } |
| |
| if (primary === 'never' && valueNode.before !== '') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.before = ''; |
| } else { |
| complain(messages.rejectedOpening, openingIndex); |
| } |
| } |
| |
| if (isSingleLine && primary === 'always-single-line' && valueNode.before !== ' ') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.before = ' '; |
| } else { |
| complain(messages.expectedOpeningSingleLine, openingIndex); |
| } |
| } |
| |
| if (isSingleLine && primary === 'never-single-line' && valueNode.before !== '') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.before = ''; |
| } else { |
| complain(messages.rejectedOpeningSingleLine, openingIndex); |
| } |
| } |
| |
| // Check closing ... |
| |
| const closingIndex = valueNode.sourceIndex + functionString.length - 2; |
| |
| if (primary === 'always' && valueNode.after !== ' ') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.after = ' '; |
| } else { |
| complain(messages.expectedClosing, closingIndex); |
| } |
| } |
| |
| if (primary === 'never' && valueNode.after !== '') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.after = ''; |
| } else { |
| complain(messages.rejectedClosing, closingIndex); |
| } |
| } |
| |
| if (isSingleLine && primary === 'always-single-line' && valueNode.after !== ' ') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.after = ' '; |
| } else { |
| complain(messages.expectedClosingSingleLine, closingIndex); |
| } |
| } |
| |
| if (isSingleLine && primary === 'never-single-line' && valueNode.after !== '') { |
| if (context.fix) { |
| hasFixed = true; |
| valueNode.after = ''; |
| } else { |
| complain(messages.rejectedClosingSingleLine, closingIndex); |
| } |
| } |
| }); |
| |
| if (hasFixed) { |
| setDeclarationValue(decl, parsedValue.toString()); |
| } |
| |
| /** |
| * @param {string} message |
| * @param {number} offset |
| */ |
| function complain(message, offset) { |
| report({ |
| ruleName, |
| result, |
| message, |
| node: decl, |
| index: declarationValueIndex(decl) + offset, |
| }); |
| } |
| }); |
| }; |
| }; |
| |
| rule.ruleName = ruleName; |
| rule.messages = messages; |
| module.exports = rule; |