tailwind_merge 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/tailwind_merge/class_utils.rb +39 -57
- data/lib/tailwind_merge/config.rb +3 -1
- data/lib/tailwind_merge/modifier_utils.rb +21 -25
- data/lib/tailwind_merge/validators.rb +3 -5
- data/lib/tailwind_merge/version.rb +1 -1
- data/lib/tailwind_merge.rb +30 -36
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3334600a104bc6b8efbe365c3be28e32944468f9cb5b1255fdefd4d106dd9540
|
4
|
+
data.tar.gz: 2f8c9a9b989c3e808767516bff6b040ed0c3a092a5fc427aaacaae4904523bcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
34
|
-
|
35
|
-
|
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
|
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 [
|
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 { |
|
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 |
|
78
|
-
process_classes_recursively(class_group, class_map, class_group_id
|
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 |
|
82
|
+
class_group_entries.map do |class_group_id, class_group|
|
88
83
|
prefixed_class_group = class_group.map do |class_definition|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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:
|
8
|
-
separator ||= ":"
|
7
|
+
def split_modifiers(class_name, separator: ":")
|
9
8
|
separator_length = separator.length
|
10
|
-
|
11
|
-
|
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 =
|
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 ==
|
21
|
-
modifiers << class_name[modifier_start
|
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
|
-
|
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
|
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
|
40
|
-
|
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.
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
59
|
+
unsorted_modifiers << modifier
|
64
60
|
end
|
65
61
|
end
|
66
62
|
|
67
|
-
sorted_modifiers.
|
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
|
-
|
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)
|
data/lib/tailwind_merge.rb
CHANGED
@@ -19,7 +19,7 @@ module TailwindMerge
|
|
19
19
|
SPLIT_CLASSES_REGEX = /\s+/
|
20
20
|
|
21
21
|
def initialize(config: {})
|
22
|
-
@config = if config.
|
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
|
-
|
34
|
-
classes = classes.compact.join(" ")
|
35
|
-
end
|
33
|
+
normalized = classes.is_a?(Array) ? classes.compact.join(" ") : classes.to_s
|
36
34
|
|
37
|
-
@cache.getset(
|
38
|
-
merge_class_list(
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
result = ""
|
46
|
+
trimmed = class_list.strip
|
47
|
+
return "" if trimmed.empty?
|
52
48
|
|
53
|
-
|
49
|
+
class_groups_in_conflict = Set.new
|
54
50
|
|
55
|
-
|
56
|
-
break if i < 0
|
51
|
+
merged_classes = []
|
57
52
|
|
58
|
-
|
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
|
-
|
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
|
-
|
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
|
67
|
-
#
|
68
|
-
|
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
|
-
#
|
77
|
-
|
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
|
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
|
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 |
|
99
|
-
class_groups_in_conflict
|
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
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
i -= 1
|
98
|
+
# Tailwind class not in conflict
|
99
|
+
merged_classes << original_class_name
|
106
100
|
end
|
107
101
|
|
108
|
-
|
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.
|
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-
|
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.
|
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.
|
82
|
-
source_code_uri: https://github.com/gjtorikian/tailwind_merge/tree/v0.
|
83
|
-
changelog_uri: https://github.com/gjtorikian/tailwind_merge/blob/v0.
|
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.
|
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:
|