Skip to content

Commit

Permalink
Merge pull request #39 from nebulab/elia+francesco/tw-class-sorter
Browse files Browse the repository at this point in the history
Add support for sorting TailwindCSS class names
  • Loading branch information
elia authored Dec 29, 2023
2 parents 246816c + 2624861 commit b1ec7f9
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/spec/reports/
/tmp/
/Gemfile.lock
/test/fixtures/tailwindcss/class_sorting.css
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).

In order to run a specific test, use the following command:

```bash
m test/erb/test_formatter.rb:123
```

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/nebulab/erb-formatter.
Expand Down
3 changes: 3 additions & 0 deletions erb-formatter.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]

spec.add_dependency "syntax_tree", '~> 6.0'

spec.add_development_dependency "tailwindcss-rails", "~> 2.0"
spec.add_development_dependency "m", "~> 1.0"
end
15 changes: 10 additions & 5 deletions lib/erb/formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,15 @@ def self.format(source, filename: nil)
new(source, filename: filename).html
end

def initialize(source, line_width: 80, single_class_per_line: false, filename: nil, debug: $DEBUG)
def initialize(source, line_width: 80, single_class_per_line: false, filename: nil, css_class_sorter: nil, debug: $DEBUG)
@original_source = source
@filename = filename || '(erb)'
@line_width = line_width
@source = remove_front_matter source.dup
@html = +""
@debug = debug
@single_class_per_line = single_class_per_line
@css_class_sorter = css_class_sorter

html.extend DebugShovel if @debug

Expand Down Expand Up @@ -125,7 +126,9 @@ def format_attributes(tag_name, attrs, tag_closing)
return "" if attrs.strip.empty?

plain_attrs = attrs.tr("\n", " ").squeeze(" ").gsub(erb_tags_regexp, erb_tags)
return " #{plain_attrs}" if "<#{tag_name} #{plain_attrs}#{tag_closing}".size <= line_width
within_line_width = "<#{tag_name} #{plain_attrs}#{tag_closing}".size <= line_width

return " #{plain_attrs}" if within_line_width && !@css_class_sorter && !plain_attrs.match?(/ class=/)

attr_html = ""
tag_stack_push(['attr='], attrs)
Expand All @@ -140,8 +143,10 @@ def format_attributes(tag_name, attrs, tag_closing)
end

value_parts = value[1...-1].strip.split(/\s+/)
value_parts.sort_by!(&@css_class_sorter) if name == 'class' && @css_class_sorter

full_attr = indented("#{name}=#{value[0]}#{value_parts.join(" ")}#{value[-1]}")
full_attr = "#{name}=#{value[0]}#{value_parts.join(" ")}#{value[-1]}"
full_attr = within_line_width ? " #{full_attr}" : indented(full_attr)

if full_attr.size > line_width && MULTILINE_ATTR_NAMES.include?(name) && attr.match?(QUOTED_ATTR)
attr_html << indented("#{name}=#{value[0]}")
Expand All @@ -165,14 +170,14 @@ def format_attributes(tag_name, attrs, tag_closing)
end

tag_stack_pop('attr"', value)
attr_html << indented(value[-1])
attr_html << (within_line_width ? value[-1] : indented(value[-1]))
else
attr_html << full_attr
end
end

tag_stack_pop(['attr='], attrs)
attr_html << indented("")
attr_html << indented("") unless within_line_width
attr_html
end

Expand Down
33 changes: 28 additions & 5 deletions lib/erb/formatter/command_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
require 'optparse'

class ERB::Formatter::CommandLine
def self.tailwindcss_class_sorter(css_path)
css = File.read(css_path)

css = css.tr("\n", " ").gsub(%r{\/\*.*?\*\/},"") # remove comments
css = css.gsub(%r<@media.*?\{>, "") # strip media queries
css = css.scan(%r<(?:^|\}|\{) *(\S.*?) *\{>).join(" ") # extract selectors
classes = css.tr(","," ").split(" ").grep(/\./).uniq.map { _1.split('.').last.gsub("\\", "") }
indexed_classes = Hash[classes.zip((0...classes.size).to_a)]

->(class_name) do
indexed_classes[class_name] || classes.index { _1.start_with?(class_name) } || -1
end
end

attr_reader :write, :filename, :read_stdin

Expand Down Expand Up @@ -41,6 +54,10 @@ def initialize(argv, stdin: $stdin)
@single_class_per_line = value
end

parser.on("--tailwind-output-path PATH", "Set the path to the tailwind output file") do |value|
@tailwind_output_path = value
end

parser.on("--[no-]debug", "Enable debug mode") do |value|
$DEBUG = value
end
Expand All @@ -61,10 +78,6 @@ def ignore_list
@ignore_list ||= ERB::Formatter::IgnoreList.new
end

def ignore?(filename)

end

def run
if read_stdin
abort "Can't read both stdin and a list of files" unless @argv.empty?
Expand All @@ -77,11 +90,21 @@ def run
end
end

if @tailwind_output_path
css_class_sorter = self.class.tailwindcss_class_sorter(@tailwind_output_path)
end

files.each do |(filename, code)|
if ignore_list.should_ignore_file? filename
print code unless write
else
html = ERB::Formatter.new(code, filename: filename, line_width: @width || 80, single_class_per_line: @single_class_per_line)
html = ERB::Formatter.new(
code,
filename: filename,
line_width: @width || 80,
single_class_per_line: @single_class_per_line,
css_class_sorter: css_class_sorter
)

if write
File.write(filename, html)
Expand Down
27 changes: 26 additions & 1 deletion test/erb/test_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_fixtures
expected_path = erb_path.chomp('.erb') + '.expected.erb'

# File.write expected_path, ERB::Formatter.format(File.read(erb_path))
assert_equal(File.read(expected_path), ERB::Formatter.format(File.read(erb_path)), "Formatting of #{erb_path} failed")
assert_equal(File.read(expected_path), ERB::Formatter.new(File.read(erb_path)).to_s, "Formatting of #{erb_path} failed")
end
end

Expand Down Expand Up @@ -156,4 +156,29 @@ def test_format_ruby_with_long_lines_and_larger_line_width
).to_s,
)
end

def test_tailwindcss_class_sorting
require 'tailwindcss-rails'
require 'erb/formatter/command_line'

error_log = "#{__dir__}/../../tmp/tailwindcss.err.log"
Dir.mkdir(File.dirname(error_log)) unless File.exist?(File.dirname(error_log))

system(
Tailwindcss::Commands.executable,
"--content", "#{__dir__}/../fixtures/tailwindcss/class_sorting.html.erb",
"--output", "#{__dir__}/../fixtures/tailwindcss/class_sorting.css",
err: error_log,
) || raise("Failed to generate tailwindcss output:\n#{File.read(error_log)}")

css_class_sorter = ERB::Formatter::CommandLine.tailwindcss_class_sorter("#{__dir__}/../fixtures/tailwindcss/class_sorting.css")

assert_equal(
File.read("#{__dir__}/../fixtures/tailwindcss/class_sorting.html.expected.erb"),
ERB::Formatter.new(
File.read("#{__dir__}/../fixtures/tailwindcss/class_sorting.html.erb"),
css_class_sorter: css_class_sorter,
).to_s,
)
end
end
14 changes: 14 additions & 0 deletions test/fixtures/tailwindcss/class_sorting.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="pt-2 p-4">
</div>

<div class="text-gray-700 shadow-md p-3 border-gray-300 ml-4 h-24 flex border-2">
</div>

<div class="hover:opacity-75 opacity-50 hover:scale-150 scale-125">
</div>

<div class="lg:grid-cols-4 grid sm:grid-cols-3 grid-cols-2">
</div>

<div class="p-3 shadow-xl select2-dropdown">
</div>
16 changes: 16 additions & 0 deletions test/fixtures/tailwindcss/class_sorting.html.expected.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div class="p-4 pt-2">
</div>

<div
class="ml-4 flex h-24 border-2 border-gray-300 p-3 text-gray-700 shadow-md"
>
</div>

<div class="scale-125 opacity-50 hover:scale-150 hover:opacity-75">
</div>

<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
</div>

<div class="select2-dropdown p-3 shadow-xl">
</div>

0 comments on commit b1ec7f9

Please sign in to comment.