tailwind_merge 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9e79ce87f36904f2dd66beecff8d32e83b97025d729f6c324f14da53aca7c8e
4
- data.tar.gz: 6ca2a49f5f9039028a9f6677bca5ec2f8765c52887680dd41c47449fe1770f28
3
+ metadata.gz: 3334600a104bc6b8efbe365c3be28e32944468f9cb5b1255fdefd4d106dd9540
4
+ data.tar.gz: 2f8c9a9b989c3e808767516bff6b040ed0c3a092a5fc427aaacaae4904523bcc
5
5
  SHA512:
6
- metadata.gz: 20b3f51eb10c68b29b5f4354570ba6cdfbcab13103d7a45f213aecdaaaa50f5c9876b676f6a217c4b0a98335cffce007f1926f0f74b2c98af0155174b48c13d2
7
- data.tar.gz: fe5786fa019198ca80233bceec9acdd5d9bd6d8098907143600d1df1e5a7a2b6525daa329c146c6d741c1df0dd276f7a87734f718cd865b7ad734d374bc6feed
6
+ metadata.gz: 7f8ad1a5e8dfc2386b81cf5f528e2b1b5ded8b9644dd942e777e3b356b0807d74397400c090b52aec68d54e93bc0bd8d672f9872795dd300b7e2b06b543701af
7
+ data.tar.gz: 33cc99d39060f7f2e6827d48f99bce9989df5771f0174eea1994133a25e05115855de5e4f138b763f7197801595c3ab8e13802291675786f782f7246429079dd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # [v0.16.0] - 25-01-2025
2
+ ## What's Changed
3
+ * Fix and improve implementation by @w-masahiro-ct in https://github.com/gjtorikian/tailwind_merge/pull/50
4
+
5
+
6
+ **Full Changelog**: https://github.com/gjtorikian/tailwind_merge/compare/v0.15.0...v0.16.0
1
7
  # [v0.15.0] - 23-01-2025
2
8
  ## What's Changed
3
9
  * Improve gemspec by @w-masahiro-ct in https://github.com/gjtorikian/tailwind_merge/pull/41
@@ -16,7 +16,7 @@ module TailwindMerge
16
16
  def class_group_id(class_name)
17
17
  class_parts = class_name.split(CLASS_PART_SEPARATOR)
18
18
 
19
- # Classes like `-inset-1` produce an empty string as first classPart.
19
+ # Classes like `-inset-1` produce an empty string as first class_part.
20
20
  # Assume that classes for negative values are used correctly and remove it from class_parts.
21
21
  class_parts.shift if class_parts.first == "" && class_parts.length != 1
22
22
 
@@ -30,9 +30,10 @@ module TailwindMerge
30
30
 
31
31
  next_class_part_object = class_part_object[:next_part][current_class_part]
32
32
 
33
- class_group_from_next_class_part = next_class_part_object ? get_group_recursive(class_parts[1..-1], next_class_part_object) : nil
34
-
35
- return class_group_from_next_class_part if class_group_from_next_class_part
33
+ if next_class_part_object
34
+ class_group_from_next_class_part = get_group_recursive(class_parts.drop(1), next_class_part_object)
35
+ return class_group_from_next_class_part if class_group_from_next_class_part
36
+ end
36
37
 
37
38
  return if class_part_object[:validators].empty?
38
39
 
@@ -40,29 +41,23 @@ module TailwindMerge
40
41
 
41
42
  result = class_part_object[:validators].find do |v|
42
43
  validator = v[:validator]
43
-
44
- if from_theme?(validator)
45
- validator.call(@config)
46
- else
47
- validator.call(class_rest)
48
- end
44
+ from_theme?(validator) ? validator.call(@config) : validator.call(class_rest)
49
45
  end
50
46
 
51
- result.nil? ? result : result[:class_group_id]
47
+ result&.fetch(:class_group_id, nil)
52
48
  end
53
49
 
54
50
  def get_conflicting_class_group_ids(class_group_id, has_postfix_modifier)
55
51
  conflicts = @config[:conflicting_class_groups][class_group_id] || []
56
52
 
57
53
  if has_postfix_modifier && @config[:conflicting_class_group_modifiers][class_group_id]
58
- return [...conflicts, ...@config[:conflicting_class_group_modifiers][class_group_id]]
54
+ return [*conflicts, *@config[:conflicting_class_group_modifiers][class_group_id]]
59
55
  end
60
56
 
61
57
  conflicts
62
58
  end
63
59
 
64
60
  private def create_class_map(config)
65
- theme = config[:theme]
66
61
  prefix = config[:prefix]
67
62
  class_map = {
68
63
  next_part: {},
@@ -70,12 +65,12 @@ module TailwindMerge
70
65
  }
71
66
 
72
67
  prefixed_class_group_entries = get_prefixed_class_group_entries(
73
- config[:class_groups].map { |cg| [cg[0], cg[1]] },
68
+ config[:class_groups].map { |group_id, group_classes| [group_id, group_classes] },
74
69
  prefix,
75
70
  )
76
71
 
77
- prefixed_class_group_entries.each do |(class_group_id, class_group)|
78
- process_classes_recursively(class_group, class_map, class_group_id, theme)
72
+ prefixed_class_group_entries.each do |class_group_id, class_group|
73
+ process_classes_recursively(class_group, class_map, class_group_id)
79
74
  end
80
75
 
81
76
  class_map
@@ -84,53 +79,43 @@ module TailwindMerge
84
79
  private def get_prefixed_class_group_entries(class_group_entries, prefix)
85
80
  return class_group_entries if prefix.nil?
86
81
 
87
- class_group_entries.map do |(class_group_id, class_group)|
82
+ class_group_entries.map do |class_group_id, class_group|
88
83
  prefixed_class_group = class_group.map do |class_definition|
89
- next("#{prefix}#{class_definition}") if class_definition.is_a?(String)
90
-
91
- next(class_definition.transform_keys { |key| "#{prefix}#{key}" }) if class_definition.is_a?(Hash)
92
-
93
- class_definition
84
+ if class_definition.is_a?(String)
85
+ "#{prefix}#{class_definition}"
86
+ elsif class_definition.is_a?(Hash)
87
+ class_definition.transform_keys { |key| "#{prefix}#{key}" }
88
+ else
89
+ class_definition
90
+ end
94
91
  end
95
92
 
96
93
  [class_group_id, prefixed_class_group]
97
94
  end
98
95
  end
99
96
 
100
- private def process_classes_recursively(class_group, class_part_object, class_group_id, theme)
97
+ private def process_classes_recursively(class_group, class_part_object, class_group_id)
101
98
  class_group.each do |class_definition|
102
99
  if class_definition.is_a?(String)
103
100
  class_part_object_to_edit = class_definition.empty? ? class_part_object : get_class_part(class_part_object, class_definition)
104
101
  class_part_object_to_edit[:class_group_id] = class_group_id
105
- next
106
- end
107
-
108
- if class_definition.is_a?(Proc)
102
+ elsif class_definition.is_a?(Proc)
109
103
  if from_theme?(class_definition)
104
+ process_classes_recursively(class_definition.call(@config), class_part_object, class_group_id)
105
+ else
106
+ class_part_object[:validators] << {
107
+ validator: class_definition,
108
+ class_group_id: class_group_id,
109
+ }
110
+ end
111
+ else
112
+ class_definition.each do |key, nested_class_group|
110
113
  process_classes_recursively(
111
- class_definition.call(@config),
112
- class_part_object,
114
+ nested_class_group,
115
+ get_class_part(class_part_object, key),
113
116
  class_group_id,
114
- theme,
115
117
  )
116
- next
117
118
  end
118
-
119
- class_part_object[:validators].push({
120
- validator: class_definition,
121
- class_group_id: class_group_id,
122
- })
123
-
124
- next
125
- end
126
-
127
- class_definition.each do |(key, class_group)|
128
- process_classes_recursively(
129
- class_group,
130
- get_class_part(class_part_object, key),
131
- class_group_id,
132
- theme,
133
- )
134
119
  end
135
120
  end
136
121
  end
@@ -153,16 +138,13 @@ module TailwindMerge
153
138
  end
154
139
 
155
140
  private def get_group_id_for_arbitrary_property(class_name)
156
- if ARBITRARY_PROPERTY_REGEX.match?(class_name)
157
- match = ARBITRARY_PROPERTY_REGEX.match(class_name) || ""
158
- arbitrary_property_class_name = match[1] || ""
159
- property = arbitrary_property_class_name[0...arbitrary_property_class_name.index(":")]
160
-
161
- if !property.nil? && !property.empty?
162
- # uses two dots here because one dot is used as prefix for class groups in plugins
163
- "arbitrary..#{property}"
164
- end
165
- end
141
+ match = ARBITRARY_PROPERTY_REGEX.match(class_name)
142
+ return unless match
143
+
144
+ property = match[1].to_s.split(":", 2).first
145
+
146
+ # Use two dots here because one dot is used as prefix for class groups in plugins
147
+ "arbitrary..#{property}" if property && !property.empty?
166
148
  end
167
149
 
168
150
  private def from_theme?(validator)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
4
+
3
5
  module TailwindMerge
4
6
  module Config
5
7
  include Validators
@@ -30,7 +32,7 @@ module TailwindMerge
30
32
  "skew",
31
33
  "space",
32
34
  "translate",
33
- ]
35
+ ].freeze
34
36
  THEME_KEYS.each do |key|
35
37
  const_set(key.upcase.tr("-", "_"), ->(config) { config[:theme].fetch(key, nil) })
36
38
  end
@@ -4,21 +4,20 @@ module TailwindMerge
4
4
  module ModifierUtils
5
5
  IMPORTANT_MODIFIER = "!"
6
6
 
7
- def split_modifiers(class_name, separator: nil)
8
- separator ||= ":"
7
+ def split_modifiers(class_name, separator: ":")
9
8
  separator_length = separator.length
10
- seperator_is_single_char = separator_length == 1
11
- first_seperator_char = separator[0]
9
+ separator_is_single_char = (separator_length == 1)
10
+ first_separator_char = separator[0]
12
11
 
13
12
  modifiers = []
14
13
  bracket_depth = 0
15
14
  modifier_start = 0
16
- postfix_modifier_position = 0
15
+ postfix_modifier_position = nil
17
16
 
18
17
  class_name.each_char.with_index do |char, index|
19
18
  if bracket_depth.zero?
20
- if char == first_seperator_char && (seperator_is_single_char || class_name[index..(index + separator_length - 1)] == separator)
21
- modifiers << class_name[modifier_start..index]
19
+ if char == first_separator_char && (separator_is_single_char || class_name[index, separator_length] == separator)
20
+ modifiers << class_name[modifier_start...index]
22
21
  modifier_start = index + separator_length
23
22
  next
24
23
  elsif char == "/"
@@ -27,17 +26,17 @@ module TailwindMerge
27
26
  end
28
27
  end
29
28
 
30
- if char == "["
31
- bracket_depth += 1
32
- elsif char == "]"
33
- bracket_depth -= 1
34
- end
29
+ bracket_depth += 1 if char == "["
30
+ bracket_depth -= 1 if char == "]"
35
31
  end
36
32
 
37
- base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..-1]
33
+ base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..]
38
34
  has_important_modifier = base_class_name_with_important_modifier.start_with?(IMPORTANT_MODIFIER)
39
- base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..-1] : base_class_name_with_important_modifier
40
- maybe_postfix_modifier_position = postfix_modifier_position && postfix_modifier_position > modifier_start ? postfix_modifier_position - modifier_start : false
35
+ base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..] : base_class_name_with_important_modifier
36
+
37
+ maybe_postfix_modifier_position = if postfix_modifier_position && postfix_modifier_position > modifier_start
38
+ postfix_modifier_position - modifier_start
39
+ end
41
40
 
42
41
  [modifiers, has_important_modifier, base_class_name, maybe_postfix_modifier_position]
43
42
  end
@@ -46,25 +45,22 @@ module TailwindMerge
46
45
  # - Predefined modifiers are sorted alphabetically
47
46
  # - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
48
47
  def sort_modifiers(modifiers)
49
- if modifiers.length <= 1
50
- return modifiers
51
- end
48
+ return modifiers if modifiers.size <= 1
52
49
 
53
50
  sorted_modifiers = []
54
51
  unsorted_modifiers = []
55
52
 
56
53
  modifiers.each do |modifier|
57
- is_arbitrary_variant = modifier[0] == "["
58
-
59
- if is_arbitrary_variant
60
- sorted_modifiers.push(unsorted_modifiers.sort, modifier)
61
- unsorted_modifiers = []
54
+ if modifier.start_with?("[")
55
+ sorted_modifiers.concat(unsorted_modifiers.sort)
56
+ sorted_modifiers << modifier
57
+ unsorted_modifiers.clear
62
58
  else
63
- unsorted_modifiers.push(modifier)
59
+ unsorted_modifiers << modifier
64
60
  end
65
61
  end
66
62
 
67
- sorted_modifiers.push(...unsorted_modifiers.sort)
63
+ sorted_modifiers.concat(unsorted_modifiers.sort)
68
64
 
69
65
  sorted_modifiers
70
66
  end
@@ -8,12 +8,10 @@ module TailwindMerge
8
8
  def arbitrary_value?(class_part, label, test_value)
9
9
  match = ARBITRARY_VALUE_REGEX.match(class_part)
10
10
  return false unless match
11
+ return test_value.call(match[2]) if match[1].nil?
12
+ return label == match[1] if label.is_a?(String)
11
13
 
12
- unless match[1].nil?
13
- return label.is_a?(Set) ? label.include?(match[1]) : label == match[1]
14
- end
15
-
16
- test_value.call(match[2])
14
+ label.include?(match[1])
17
15
  end
18
16
 
19
17
  def numeric?(x)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TailwindMerge
4
- VERSION = "0.15.0"
4
+ VERSION = "0.16.0"
5
5
  end
@@ -19,7 +19,7 @@ module TailwindMerge
19
19
  SPLIT_CLASSES_REGEX = /\s+/
20
20
 
21
21
  def initialize(config: {})
22
- @config = if config.fetch(:theme, nil)
22
+ @config = if config.key?(:theme)
23
23
  merge_configs(config)
24
24
  else
25
25
  TailwindMerge::Config::DEFAULTS.merge(config)
@@ -30,12 +30,10 @@ module TailwindMerge
30
30
  end
31
31
 
32
32
  def merge(classes)
33
- if classes.is_a?(Array)
34
- classes = classes.compact.join(" ")
35
- end
33
+ normalized = classes.is_a?(Array) ? classes.compact.join(" ") : classes.to_s
36
34
 
37
- @cache.getset(classes) do
38
- merge_class_list(classes).freeze
35
+ @cache.getset(normalized) do
36
+ merge_class_list(normalized).freeze
39
37
  end
40
38
  end
41
39
 
@@ -45,37 +43,38 @@ module TailwindMerge
45
43
  # @example 'float'
46
44
  # @example 'hover:focus:bg-color'
47
45
  # @example 'md:!pr'
48
- class_groups_in_conflict = []
49
- class_names = class_list.strip.split(SPLIT_CLASSES_REGEX)
50
-
51
- result = ""
46
+ trimmed = class_list.strip
47
+ return "" if trimmed.empty?
52
48
 
53
- i = class_names.length - 1
49
+ class_groups_in_conflict = Set.new
54
50
 
55
- loop do
56
- break if i < 0
51
+ merged_classes = []
57
52
 
58
- original_class_name = class_names[i]
53
+ trimmed.split(SPLIT_CLASSES_REGEX).reverse_each do |original_class_name|
54
+ modifiers, has_important_modifier, base_class_name, maybe_postfix_modifier_position =
55
+ split_modifiers(original_class_name, separator: @config[:separator])
59
56
 
60
- modifiers, has_important_modifier, base_class_name, maybe_postfix_modifier_position = split_modifiers(original_class_name, separator: @config[:separator])
57
+ actual_base_class_name = if maybe_postfix_modifier_position
58
+ base_class_name[0...maybe_postfix_modifier_position]
59
+ else
60
+ base_class_name
61
+ end
61
62
 
62
- actual_base_class_name = maybe_postfix_modifier_position ? base_class_name[0...maybe_postfix_modifier_position] : base_class_name
63
+ has_postfix_modifier = maybe_postfix_modifier_position ? true : false
63
64
  class_group_id = @class_utils.class_group_id(actual_base_class_name)
64
65
 
65
66
  unless class_group_id
66
- unless maybe_postfix_modifier_position
67
- # not a Tailwind class
68
- result = original_class_name + (!result.empty? ? " " + result : result)
69
- i -= 1
67
+ unless has_postfix_modifier
68
+ # Not a Tailwind class
69
+ merged_classes << original_class_name
70
70
  next
71
71
  end
72
72
 
73
73
  class_group_id = @class_utils.class_group_id(base_class_name)
74
74
 
75
75
  unless class_group_id
76
- # not a Tailwind class
77
- result = original_class_name + (!result.empty? ? " " + result : result)
78
- i -= 1
76
+ # Not a Tailwind class
77
+ merged_classes << original_class_name
79
78
  next
80
79
  end
81
80
 
@@ -87,25 +86,20 @@ module TailwindMerge
87
86
  modifier_id = has_important_modifier ? "#{variant_modifier}#{IMPORTANT_MODIFIER}" : variant_modifier
88
87
  class_id = "#{modifier_id}#{class_group_id}"
89
88
 
90
- # Tailwind class omitted due to pre-existing conflict
91
- if class_groups_in_conflict.include?(class_id)
92
- i -= 1
93
- next
94
- end
89
+ # Tailwind class omitted due to conflict
90
+ next if class_groups_in_conflict.include?(class_id)
95
91
 
96
- class_groups_in_conflict.push(class_id)
92
+ class_groups_in_conflict << class_id
97
93
 
98
- @class_utils.get_conflicting_class_group_ids(class_group_id, has_postfix_modifier).each do |group|
99
- class_groups_in_conflict.push("#{modifier_id}#{group}")
94
+ @class_utils.get_conflicting_class_group_ids(class_group_id, has_postfix_modifier).each do |conflicting_id|
95
+ class_groups_in_conflict << "#{modifier_id}#{conflicting_id}"
100
96
  end
101
97
 
102
- # no conflict!
103
- result = original_class_name + (!result.empty? ? " " + result : result)
104
-
105
- i -= 1
98
+ # Tailwind class not in conflict
99
+ merged_classes << original_class_name
106
100
  end
107
101
 
108
- result
102
+ merged_classes.reverse.join(" ")
109
103
  end
110
104
  end
111
105
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tailwind_merge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen J. Torikian
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-23 00:00:00.000000000 Z
11
+ date: 2025-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sin_lru_redux
@@ -74,15 +74,15 @@ files:
74
74
  - lib/tailwind_merge/validators.rb
75
75
  - lib/tailwind_merge/version.rb
76
76
  - tailwind_merge.gemspec
77
- homepage: https://github.com/gjtorikian/tailwind_merge/tree/v0.15.0
77
+ homepage: https://github.com/gjtorikian/tailwind_merge/tree/v0.16.0
78
78
  licenses:
79
79
  - MIT
80
80
  metadata:
81
- homepage_uri: https://github.com/gjtorikian/tailwind_merge/tree/v0.15.0
82
- source_code_uri: https://github.com/gjtorikian/tailwind_merge/tree/v0.15.0
83
- changelog_uri: https://github.com/gjtorikian/tailwind_merge/blob/v0.15.0/CHANGELOG.md
81
+ homepage_uri: https://github.com/gjtorikian/tailwind_merge/tree/v0.16.0
82
+ source_code_uri: https://github.com/gjtorikian/tailwind_merge/tree/v0.16.0
83
+ changelog_uri: https://github.com/gjtorikian/tailwind_merge/blob/v0.16.0/CHANGELOG.md
84
84
  bug_tracker_uri: https://github.com/gjtorikian/tailwind_merge/issues
85
- documentation_uri: https://rubydoc.info/gems/tailwind_merge/0.15.0
85
+ documentation_uri: https://rubydoc.info/gems/tailwind_merge/0.16.0
86
86
  funding_uri: https://github.com/sponsors/gjtorikian
87
87
  rubygems_mfa_required: 'true'
88
88
  post_install_message: