diff --git a/CHANGELOG.md b/CHANGELOG.md index de60100..be37493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Unreleased - * Put diff here + * Allow CSS functions to be used in CssParser::RuleSet#expand_dimensions_shorthand! [#126](https://github.com/premailer/css_parser/pull/126) ### Version 1.9.0 diff --git a/lib/css_parser/regexps.rb b/lib/css_parser/regexps.rb index 9b3bcf6..a76ffda 100644 --- a/lib/css_parser/regexps.rb +++ b/lib/css_parser/regexps.rb @@ -58,6 +58,22 @@ def self.regex_possible_values(*values) RE_BORDER_STYLE = /(\s*^)?(none|hidden|dotted|dashed|solid|double|dot-dash|dot-dot-dash|wave|groove|ridge|inset|outset)(\s*$)?/imx.freeze RE_BORDER_UNITS = Regexp.union(BOX_MODEL_UNITS_RX, /(thin|medium|thick)/i) + # Functions like calc, var, clamp, etc. + RE_FUNCTIONS = / + ( + [a-z0-9-]+ # function name + ) + (?> + \( # opening parenthesis + (?: + ([^()]+) + | # recursion via subexpression + \g<0> + )* + \) # closing parenthesis + ) + /imx.freeze + # Patterns for specificity calculations NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX_NC = / (?:\.\w+) # classes diff --git a/lib/css_parser/rule_set.rb b/lib/css_parser/rule_set.rb index 793e80f..bd880a8 100644 --- a/lib/css_parser/rule_set.rb +++ b/lib/css_parser/rule_set.rb @@ -24,6 +24,8 @@ class RuleSet ['border-width', %w[border-top-width border-right-width border-bottom-width border-left-width]] ].freeze + WHITESPACE_REPLACEMENT = '___SPACE___' + class Declarations class Value attr_reader :value @@ -358,7 +360,7 @@ def expand_dimensions_shorthand! # :nodoc: # TODO: rgba, hsl, hsla value.gsub!(RE_COLOUR) { |c| c.gsub(/(\s*,\s*)/, ',') } - matches = value.strip.split(/\s+/) + matches = split_value_preserving_function_whitespace(value) case matches.length when 1 @@ -374,8 +376,7 @@ def expand_dimensions_shorthand! # :nodoc: raise ArgumentError, "Cannot parse #{value}" end - t, r, b, l = values - replacement = {top => t, right => r, bottom => b, left => l} + replacement = [top, right, bottom, left].zip(values).to_h declarations.replace_declaration!(property, replacement, preserve_importance: true) end @@ -634,6 +635,19 @@ def parse_selectors!(selectors) # :nodoc: s end end + + def split_value_preserving_function_whitespace(value) + split_value = value.gsub(RE_FUNCTIONS) do |c| + c.gsub!(/\s+/, WHITESPACE_REPLACEMENT) + c + end + + matches = split_value.strip.split(/\s+/) + + matches.each do |c| + c.gsub!(WHITESPACE_REPLACEMENT, ' ') + end + end end class OffsetAwareRuleSet < RuleSet diff --git a/test/test_rule_set_expanding_shorthand.rb b/test/test_rule_set_expanding_shorthand.rb index 2d41018..792285e 100644 --- a/test/test_rule_set_expanding_shorthand.rb +++ b/test/test_rule_set_expanding_shorthand.rb @@ -296,6 +296,54 @@ def test_expanding_important_shorthand_with_replaced_properties assert_equal expected_declarations, declarations end + def test_functions_with_many_spaces + shorthand = 'margin: calc(1em / 4 * var(--foo));' + declarations = expand_declarations(shorthand) + expected_declarations = { + 'margin-top' => 'calc(1em / 4 * var(--foo))', + 'margin-bottom' => 'calc(1em / 4 * var(--foo))', + 'margin-left' => 'calc(1em / 4 * var(--foo))', + 'margin-right' => 'calc(1em / 4 * var(--foo))' + } + assert_equal expected_declarations, declarations + end + + def test_functions_with_no_spaces + shorthand = 'margin: calc(1em/4*4);' + declarations = expand_declarations(shorthand) + expected_declarations = { + 'margin-top' => 'calc(1em/4*4)', + 'margin-bottom' => 'calc(1em/4*4)', + 'margin-left' => 'calc(1em/4*4)', + 'margin-right' => 'calc(1em/4*4)' + } + assert_equal expected_declarations, declarations + end + + def test_functions_with_one_space + shorthand = 'margin: calc(1em /4);' + declarations = expand_declarations(shorthand) + expected_declarations = { + 'margin-top' => 'calc(1em /4)', + 'margin-bottom' => 'calc(1em /4)', + 'margin-left' => 'calc(1em /4)', + 'margin-right' => 'calc(1em /4)' + } + assert_equal expected_declarations, declarations + end + + def test_functions_with_commas + shorthand = 'margin: clamp(1rem, 2.5vw, 2rem)' + declarations = expand_declarations(shorthand) + expected_declarations = { + 'margin-top' => 'clamp(1rem, 2.5vw, 2rem)', + 'margin-bottom' => 'clamp(1rem, 2.5vw, 2rem)', + 'margin-left' => 'clamp(1rem, 2.5vw, 2rem)', + 'margin-right' => 'clamp(1rem, 2.5vw, 2rem)' + } + assert_equal expected_declarations, declarations + end + protected def expand_declarations(declarations)