tailwind_merge 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -1
- data/Gemfile +2 -0
- data/README.md +23 -23
- data/lib/tailwind_merge/config.rb +1 -0
- data/lib/tailwind_merge/modifier_utils.rb +64 -0
- data/lib/tailwind_merge/version.rb +1 -1
- data/lib/tailwind_merge.rb +4 -65
- data/script/generate_changelog +3 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e16a945fd3a3872687ebacc6fa3a18ca60424e735e31f28461e500116c7fd5f
|
4
|
+
data.tar.gz: 3b8b7ef9495914b7004cada2918c3ecb22099f1279607fe6dc1cd47db3018be6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f13054805b9efa04fa50dd84f4ffb57823f026d865e050f7617a89bc4374cd520950887defe714c49e1d50ed9a88e8a0bb27383b0a67f4ba4cdba6f8b8918667
|
7
|
+
data.tar.gz: af31f90a35a33964f9fc5f0e141310c5437a322be4b906123de557e9c1a925bcea8709dc6888035905bb9eb3dbf29fb822ae0835e0529fbf36d354928445b538
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,54 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
2
|
|
3
|
+
## [v0.4.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.4.0) (2022-11-11)
|
3
4
|
|
5
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.3.1...v0.4.0)
|
4
6
|
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Support custom seperators [\#5](https://github.com/gjtorikian/tailwind_merge/pull/5) ([gjtorikian](https://github.com/gjtorikian))
|
10
|
+
|
11
|
+
## [v0.3.1](https://github.com/gjtorikian/tailwind_merge/tree/v0.3.1) (2022-11-03)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.3.0...v0.3.1)
|
14
|
+
|
15
|
+
## [v0.3.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.3.0) (2022-10-20)
|
16
|
+
|
17
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.2.0...v0.3.0)
|
18
|
+
|
19
|
+
**Merged pull requests:**
|
20
|
+
|
21
|
+
- Support Tailwind 3.2 [\#4](https://github.com/gjtorikian/tailwind_merge/pull/4) ([gjtorikian](https://github.com/gjtorikian))
|
22
|
+
|
23
|
+
## [v0.2.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.2.0) (2022-10-14)
|
24
|
+
|
25
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.3...v0.2.0)
|
26
|
+
|
27
|
+
## [v0.1.3](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.3) (2022-10-11)
|
28
|
+
|
29
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.2...v0.1.3)
|
30
|
+
|
31
|
+
## [v0.1.2](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.2) (2022-10-11)
|
32
|
+
|
33
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.1...v0.1.2)
|
34
|
+
|
35
|
+
## [v0.1.1](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.1) (2022-10-11)
|
36
|
+
|
37
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.0...v0.1.1)
|
38
|
+
|
39
|
+
**Closed issues:**
|
40
|
+
|
41
|
+
- Should arrays be supported? [\#1](https://github.com/gjtorikian/tailwind_merge/issues/1)
|
42
|
+
|
43
|
+
**Merged pull requests:**
|
44
|
+
|
45
|
+
- Fix h-min [\#3](https://github.com/gjtorikian/tailwind_merge/pull/3) ([gjtorikian](https://github.com/gjtorikian))
|
46
|
+
- Fix example in README [\#2](https://github.com/gjtorikian/tailwind_merge/pull/2) ([marcoroth](https://github.com/marcoroth))
|
47
|
+
|
48
|
+
## [v0.1.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.0) (2022-08-15)
|
49
|
+
|
50
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/e748f00d53e86ece8ce2543735f3327cb30c1959...v0.1.0)
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -27,9 +27,7 @@ If you use Tailwind with a component-based UI renderer (like [ViewComponent](htt
|
|
27
27
|
|
28
28
|
```html
|
29
29
|
<!-- app/components/confirm_email_component.html.erb -->
|
30
|
-
<div class="border rounded px-2 py-1">
|
31
|
-
Please confirm your email address.
|
32
|
-
</div>
|
30
|
+
<div class="border rounded px-2 py-1">Please confirm your email address.</div>
|
33
31
|
```
|
34
32
|
|
35
33
|
```ruby
|
@@ -53,9 +51,9 @@ tailwind-merge overrides conflicting classes and keeps everything else untouched
|
|
53
51
|
|
54
52
|
### Optimized for speed
|
55
53
|
|
56
|
-
-
|
57
|
-
-
|
58
|
-
-
|
54
|
+
- Results get cached by default, so you don't need to worry about wasteful re-renders. The library uses a thread-safe [LRU cache](<https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)>) which stores up to 500 different results. The cache size can be modified via config options.
|
55
|
+
- Expensive computations happen upfront so that `merge` calls without a cache hit stay fast.
|
56
|
+
- These computations are called lazily on the first call to `merge` to prevent it from impacting app startup performance if it isn't used initially.
|
59
57
|
|
60
58
|
### Last conflicting class wins
|
61
59
|
|
@@ -150,10 +148,10 @@ If you're using a custom Tailwind config, you may need to configure tailwind-mer
|
|
150
148
|
|
151
149
|
The default [`twMerge`](#twmerge) function is configured in a way that you can still use it if all the following points apply to your Tailwind config:
|
152
150
|
|
153
|
-
-
|
154
|
-
-
|
155
|
-
-
|
156
|
-
-
|
151
|
+
- Only using color names which don't clash with other Tailwind class names
|
152
|
+
- Only deviating by number values from number-based Tailwind classes
|
153
|
+
- Only using font-family classes which don't clash with default font-weight classes
|
154
|
+
- Sticking to default Tailwind config for everything else
|
157
155
|
|
158
156
|
If some of these points don't apply to you, you can test whether the merge still works as intended with your custom classes. Otherwise, you need create your own custom merge function by either extending the default tailwind-merge config or using a completely custom one.
|
159
157
|
|
@@ -165,9 +163,11 @@ The `tailwind_merge` config is an object with several keys:
|
|
165
163
|
|
166
164
|
```ruby
|
167
165
|
tailwindMergeConfig = {
|
168
|
-
# ↓
|
166
|
+
# ↓ *Optional* Define how many values should be stored in cache.
|
169
167
|
cache_size: 500,
|
170
|
-
# ↓ Optional
|
168
|
+
# ↓ *Optional* modifier separator from Tailwind config
|
169
|
+
separator: ':',
|
170
|
+
# ↓ *Optional* prefix from Tailwind config
|
171
171
|
prefix: 'tw-',
|
172
172
|
theme: {
|
173
173
|
# Theme scales are defined here
|
@@ -283,17 +283,17 @@ If you modified one of these theme scales in your Tailwind config, you can add a
|
|
283
283
|
|
284
284
|
Here's a brief summary for each validator:
|
285
285
|
|
286
|
-
-
|
287
|
-
-
|
288
|
-
-
|
289
|
-
-
|
290
|
-
-
|
291
|
-
-
|
292
|
-
-
|
293
|
-
-
|
294
|
-
-
|
295
|
-
-
|
296
|
-
-
|
286
|
+
- `IS_LENGTH` checks whether a class part is a number (`3`, `1.5`), a fraction (`3/4`), a arbitrary length (`[3%]`, `[4px]`, `[length:var(--my-var)]`), or one of the strings `px`, `full` or `screen`.
|
287
|
+
- `IS_ARBITRARY_LENGTH` checks for arbitrary length values (`[3%]`, `[4px]`, `[length:var(--my-var)]`).
|
288
|
+
- `IS_INTEGER` checks for integer values (`3`) and arbitrary integer values (`[3]`).
|
289
|
+
- `IS_ARBITRARY_VALUE` checks whether the class part is enclosed in brackets (`[something]`)
|
290
|
+
- `IS_TSHIRT_SIZE`checks whether class part is a T-shirt size (`sm`, `xl`), optionally with a preceding number (`2xl`).
|
291
|
+
- `IS_ARBITRARY_SIZE` checks whether class part is an arbitrary value which starts with `size:` (`[size:200px_100px]`) which is necessary for background-size classNames.
|
292
|
+
- `IS_ARBITRARY_POSITION` checks whether class part is an arbitrary value which starts with `position:` (`[position:200px_100px]`) which is necessary for background-position classNames.
|
293
|
+
- `IS_ARBITRARY_URL` checks whether class part is an arbitrary value which starts with `url:` or `url(` (`[url('/path-to-image.png')]`, `url:var(--maybe-a-url-at-runtime)]`) which is necessary for background-image classNames.
|
294
|
+
- `IS_ARBITRARY_NUMBER` checks whether class part is an arbitrary value which starts with `number:` or is a number (`[number:var(--value)]`, `[450]`) which is necessary for font-weight classNames.
|
295
|
+
- `IS_ARBITRARY_SHADOW` checks whether class part is an arbitrary value which starts with the same pattern as a shadow value (`[0_35px_60px_-15px_rgba(0,0,0,0.3)]`), namely with two lengths separated by a underscore.
|
296
|
+
- `IS_ANY` always returns true. Be careful with this validator as it might match unwanted classes. I use it primarily to match colors or when it's certain there are no other class groups in a namespace.
|
297
297
|
|
298
298
|
## Contributing
|
299
299
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TailwindMerge
|
4
|
+
module ModifierUtils
|
5
|
+
IMPORTANT_MODIFIER = "!"
|
6
|
+
|
7
|
+
def split_modifiers(class_name, separator: nil)
|
8
|
+
separator ||= ":"
|
9
|
+
separator_length = separator.length
|
10
|
+
|
11
|
+
modifiers = []
|
12
|
+
bracket_depth = 0
|
13
|
+
modifier_start = 0
|
14
|
+
|
15
|
+
class_name.each_char.with_index do |char, index|
|
16
|
+
if bracket_depth.zero? && char == separator[0]
|
17
|
+
if separator_length == 1 || class_name[index..(index + separator_length - 1)] == separator
|
18
|
+
modifiers << class_name[modifier_start..index]
|
19
|
+
modifier_start = index + separator_length
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if class_name[index] == "["
|
24
|
+
bracket_depth += 1
|
25
|
+
elsif class_name[index] == "]"
|
26
|
+
bracket_depth -= 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..-1]
|
31
|
+
has_important_modifier = base_class_name_with_important_modifier.start_with?(IMPORTANT_MODIFIER)
|
32
|
+
base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..-1] : base_class_name_with_important_modifier
|
33
|
+
|
34
|
+
[modifiers, has_important_modifier, base_class_name]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sorts modifiers according to following schema:
|
38
|
+
# - Predefined modifiers are sorted alphabetically
|
39
|
+
# - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
|
40
|
+
def sort_modifiers(modifiers)
|
41
|
+
if modifiers.length <= 1
|
42
|
+
return modifiers
|
43
|
+
end
|
44
|
+
|
45
|
+
sorted_modifiers = []
|
46
|
+
unsorted_modifiers = []
|
47
|
+
|
48
|
+
modifiers.each do |modifier|
|
49
|
+
is_arbitrary_variant = modifier[0] == "["
|
50
|
+
|
51
|
+
if is_arbitrary_variant
|
52
|
+
sorted_modifiers.push(unsorted_modifiers.sort, modifier)
|
53
|
+
unsorted_modifiers = []
|
54
|
+
else
|
55
|
+
unsorted_modifiers.push(modifier)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
sorted_modifiers.push(...unsorted_modifiers.sort)
|
60
|
+
|
61
|
+
sorted_modifiers
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/tailwind_merge.rb
CHANGED
@@ -11,6 +11,7 @@ require_relative "tailwind_merge/version"
|
|
11
11
|
require_relative "tailwind_merge/validators"
|
12
12
|
require_relative "tailwind_merge/config"
|
13
13
|
require_relative "tailwind_merge/class_utils"
|
14
|
+
require_relative "tailwind_merge/modifier_utils"
|
14
15
|
|
15
16
|
require "strscan"
|
16
17
|
require "set"
|
@@ -18,9 +19,9 @@ require "set"
|
|
18
19
|
module TailwindMerge
|
19
20
|
class Merger
|
20
21
|
include Config
|
22
|
+
include ModifierUtils
|
21
23
|
|
22
24
|
SPLIT_CLASSES_REGEX = /\s+/
|
23
|
-
IMPORTANT_MODIFIER = "!"
|
24
25
|
|
25
26
|
def initialize(config: {})
|
26
27
|
@config = if config.fetch(:theme, nil)
|
@@ -48,7 +49,7 @@ module TailwindMerge
|
|
48
49
|
class_groups_in_conflict = Set.new
|
49
50
|
|
50
51
|
classes.strip.split(SPLIT_CLASSES_REGEX).map do |original_class_name|
|
51
|
-
modifiers, has_important_modifier, base_class_name = split_modifiers(original_class_name)
|
52
|
+
modifiers, has_important_modifier, base_class_name = split_modifiers(original_class_name, separator: @config[:separator])
|
52
53
|
|
53
54
|
class_group_id = @class_utils.class_group_id(base_class_name)
|
54
55
|
|
@@ -59,7 +60,7 @@ module TailwindMerge
|
|
59
60
|
}
|
60
61
|
end
|
61
62
|
|
62
|
-
variant_modifier = sort_modifiers(modifiers).join("")
|
63
|
+
variant_modifier = sort_modifiers(modifiers).join(":")
|
63
64
|
|
64
65
|
modifier_id = has_important_modifier ? "#{variant_modifier}#{IMPORTANT_MODIFIER}" : variant_modifier
|
65
66
|
|
@@ -89,67 +90,5 @@ module TailwindMerge
|
|
89
90
|
true
|
90
91
|
end.reverse.map { |parsed| parsed[:original_class_name] }.join(" ")
|
91
92
|
end
|
92
|
-
|
93
|
-
SPLIT_MODIFIER_REGEX = /[:\[\]]/
|
94
|
-
private def split_modifiers(class_name)
|
95
|
-
modifiers = []
|
96
|
-
|
97
|
-
bracket_depth = 0
|
98
|
-
modifier_start = 0
|
99
|
-
|
100
|
-
ss = StringScanner.new(class_name)
|
101
|
-
|
102
|
-
until ss.eos?
|
103
|
-
portion = ss.scan_until(SPLIT_MODIFIER_REGEX)
|
104
|
-
|
105
|
-
if portion.nil?
|
106
|
-
ss.terminate
|
107
|
-
next
|
108
|
-
end
|
109
|
-
pos = ss.pos - 1
|
110
|
-
if class_name[pos] == ":" && bracket_depth.zero?
|
111
|
-
next_modifier_start = pos
|
112
|
-
modifiers << class_name[modifier_start..next_modifier_start]
|
113
|
-
modifier_start = next_modifier_start + 1
|
114
|
-
elsif class_name[pos] == "["
|
115
|
-
bracket_depth += 1
|
116
|
-
elsif class_name[pos] == "]"
|
117
|
-
bracket_depth -= 1
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..-1]
|
122
|
-
has_important_modifier = base_class_name_with_important_modifier.start_with?(IMPORTANT_MODIFIER)
|
123
|
-
base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..-1] : base_class_name_with_important_modifier
|
124
|
-
|
125
|
-
[modifiers, has_important_modifier, base_class_name]
|
126
|
-
end
|
127
|
-
|
128
|
-
# Sorts modifiers according to following schema:
|
129
|
-
# - Predefined modifiers are sorted alphabetically
|
130
|
-
# - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
|
131
|
-
private def sort_modifiers(modifiers)
|
132
|
-
if modifiers.length <= 1
|
133
|
-
return modifiers
|
134
|
-
end
|
135
|
-
|
136
|
-
sorted_modifiers = []
|
137
|
-
unsorted_modifiers = []
|
138
|
-
|
139
|
-
modifiers.each do |modifier|
|
140
|
-
is_arbitrary_variant = modifier[0] == "["
|
141
|
-
|
142
|
-
if is_arbitrary_variant
|
143
|
-
sorted_modifiers.push(unsorted_modifiers.sort, modifier)
|
144
|
-
unsorted_modifiers = []
|
145
|
-
else
|
146
|
-
unsorted_modifiers.push(modifier)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
sorted_modifiers.push(...unsorted_modifiers.sort)
|
151
|
-
|
152
|
-
sorted_modifiers
|
153
|
-
end
|
154
93
|
end
|
155
94
|
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.4.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: 2022-11-
|
11
|
+
date: 2022-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lru_redux
|
@@ -138,8 +138,10 @@ files:
|
|
138
138
|
- lib/tailwind_merge.rb
|
139
139
|
- lib/tailwind_merge/class_utils.rb
|
140
140
|
- lib/tailwind_merge/config.rb
|
141
|
+
- lib/tailwind_merge/modifier_utils.rb
|
141
142
|
- lib/tailwind_merge/validators.rb
|
142
143
|
- lib/tailwind_merge/version.rb
|
144
|
+
- script/generate_changelog
|
143
145
|
- tailwind_merge.gemspec
|
144
146
|
homepage: https://www.github.com/gjtorikian/tailwind_merge
|
145
147
|
licenses:
|