Skip to content

Commit 97afbbd

Browse files
author
David Heinemeier Hansson
committed
Simplify API
1 parent 253f3af commit 97afbbd

File tree

5 files changed

+174
-43
lines changed

5 files changed

+174
-43
lines changed

lib/tailwindcss/compressor.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ def self.call(input)
1010
end
1111

1212
def initialize(options = {})
13-
@options = { paths_with_css_class_names: [ "app/views/**/*.*", "app/helpers/**/*.rb" ] }.merge(options).freeze
13+
@options = { files_with_class_names: Rails.root.glob("app/views/**/*.*") + Rails.root.glob("app/helpers/**/*.rb") }.merge(options).freeze
1414
end
1515

1616
def call(input)
17-
{ data: Tailwindcss::Purger.new(**@options).purge(input[:data]) }
17+
{ data: Tailwindcss::Purger.purge(input[:data], keeping_class_names_from_files: @options[:files_with_class_names]) }
1818
end
1919
end

lib/tailwindcss/purger.rb

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,61 @@
11
class Tailwindcss::Purger
2-
CLASS_NAME_PATTERN = /([:A-Za-z0-9_-]+)/
2+
CLASS_NAME_PATTERN = /([:A-Za-z0-9_-]+[\.]*[\\\/:A-Za-z0-9_-]*)/
33
OPENING_SELECTOR_PATTERN = /\..*\{/
44
CLOSING_SELECTOR_PATTERN = /\s*\}/
5+
NEWLINE = "\n"
56

6-
attr_reader :paths_with_css_class_names
7+
attr_reader :keep_these_class_names
78

8-
def initialize(paths_with_css_class_names:)
9-
@paths_with_css_class_names = paths_with_css_class_names
9+
class << self
10+
def purge(input, keeping_class_names_from_files:)
11+
new(extract_class_names_from(keeping_class_names_from_files)).purge(input)
12+
end
13+
14+
def extract_class_names(string)
15+
string.scan(CLASS_NAME_PATTERN).flatten.uniq.sort
16+
end
17+
18+
def extract_class_names_from(files)
19+
Array(files).flat_map { |file| extract_class_names(file.read) }.uniq.sort
20+
end
21+
end
22+
23+
def initialize(keep_these_class_names)
24+
@keep_these_class_names = keep_these_class_names
1025
end
1126

1227
def purge(input)
13-
inside_valid_selector = inside_invalid_selector = false
28+
inside_kept_selector = inside_ignored_selector = false
1429
output = []
1530

16-
input.split("\n").each do |line|
31+
input.split(NEWLINE).each do |line|
1732
case
18-
when inside_valid_selector
33+
when inside_kept_selector
1934
output << line
20-
inside_valid_selector = false if line =~ CLOSING_SELECTOR_PATTERN
21-
when inside_invalid_selector
22-
inside_invalid_selector = false if line =~ CLOSING_SELECTOR_PATTERN
23-
else
24-
if line =~ OPENING_SELECTOR_PATTERN
25-
line.remove("\u001A") =~ CLASS_NAME_PATTERN
26-
27-
if potential_css_class_names.include?($1)
28-
output << line
29-
inside_valid_selector = true
30-
else
31-
inside_invalid_selector = true
32-
end
33-
else
35+
inside_kept_selector = false if line =~ CLOSING_SELECTOR_PATTERN
36+
when inside_ignored_selector
37+
inside_ignored_selector = false if line =~ CLOSING_SELECTOR_PATTERN
38+
when line =~ OPENING_SELECTOR_PATTERN
39+
if keep_these_class_names.include? class_name_in(line)
3440
output << line
41+
inside_kept_selector = true
42+
else
43+
inside_ignored_selector = true
3544
end
45+
else
46+
output << line
3647
end
3748
end
3849

39-
output.reject { |line| line == "\n" }.join
40-
end
41-
42-
def potential_css_class_names
43-
@potential_css_class_names ||= find_potential_css_class_names_in(paths_with_css_class_names)
50+
separated_without_empty_lines(output)
4451
end
4552

4653
private
47-
def find_potential_css_class_names_in(path_patterns)
48-
files_in(path_patterns).flat_map { |file| extract_potential_css_class_names_from(file) }.flatten.uniq.sort
49-
end
50-
51-
def files_in(path_patterns)
52-
path_patterns.flat_map { |path_pattern| Rails.root.glob(path_pattern) }
54+
def class_name_in(line)
55+
CLASS_NAME_PATTERN.match(line)[1].remove("\\")
5356
end
5457

55-
def extract_potential_css_class_names_from(file)
56-
file.read.scan(CLASS_NAME_PATTERN)
58+
def separated_without_empty_lines(output)
59+
output.reject { |line| line.strip.empty? }.join(NEWLINE)
5760
end
5861
end

test/dummy/app/views/tailwinded/show.html.erb

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,119 @@
9191
</div>
9292
</div>
9393
</div>
94+
95+
<div class="relative bg-white overflow-hidden">
96+
<div class="max-w-7xl mx-auto">
97+
<div class="relative z-10 pb-8 bg-white sm:pb-16 md:pb-20 lg:max-w-2xl lg:w-full lg:pb-28 xl:pb-32">
98+
<svg class="hidden lg:block absolute right-0 inset-y-0 h-full w-48 text-white transform translate-x-1/2" fill="currentColor" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
99+
<polygon points="50,0 100,0 50,100 0,100" />
100+
</svg>
101+
102+
<div class="relative pt-6 px-4 sm:px-6 lg:px-8">
103+
<nav class="relative flex items-center justify-between sm:h-10 lg:justify-start" aria-label="Global">
104+
<div class="flex items-center flex-grow flex-shrink-0 lg:flex-grow-0">
105+
<div class="flex items-center justify-between w-full md:w-auto">
106+
<a href="#">
107+
<span class="sr-only">Workflow</span>
108+
<img class="h-8 w-auto sm:h-10" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg">
109+
</a>
110+
<div class="-mr-2 flex items-center md:hidden">
111+
<button type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500" id="main-menu" aria-haspopup="true">
112+
<span class="sr-only">Open main menu</span>
113+
<!-- Heroicon name: menu -->
114+
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
115+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
116+
</svg>
117+
</button>
118+
</div>
119+
</div>
120+
</div>
121+
<div class="hidden md:block md:ml-10 md:pr-4 md:space-x-8">
122+
<a href="#" class="font-medium text-gray-500 hover:text-gray-900">Product</a>
123+
124+
<a href="#" class="font-medium text-gray-500 hover:text-gray-900">Features</a>
125+
126+
<a href="#" class="font-medium text-gray-500 hover:text-gray-900">Marketplace</a>
127+
128+
<a href="#" class="font-medium text-gray-500 hover:text-gray-900">Company</a>
129+
130+
<a href="#" class="font-medium text-indigo-600 hover:text-indigo-500">Log in</a>
131+
</div>
132+
</nav>
133+
</div>
134+
135+
<!--
136+
Mobile menu, show/hide based on menu open state.
137+
138+
Entering: "duration-150 ease-out"
139+
From: "opacity-0 scale-95"
140+
To: "opacity-100 scale-100"
141+
Leaving: "duration-100 ease-in"
142+
From: "opacity-100 scale-100"
143+
To: "opacity-0 scale-95"
144+
-->
145+
<div class="absolute top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden">
146+
<div class="rounded-lg shadow-md bg-white ring-1 ring-black ring-opacity-5 overflow-hidden">
147+
<div class="px-5 pt-4 flex items-center justify-between">
148+
<div>
149+
<img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" alt="">
150+
</div>
151+
<div class="-mr-2">
152+
<button type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
153+
<span class="sr-only">Close main menu</span>
154+
<!-- Heroicon name: x -->
155+
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
156+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
157+
</svg>
158+
</button>
159+
</div>
160+
</div>
161+
<div role="menu" aria-orientation="vertical" aria-labelledby="main-menu">
162+
<div class="px-2 pt-2 pb-3 space-y-1" role="none">
163+
<a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Product</a>
164+
165+
<a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Features</a>
166+
167+
<a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Marketplace</a>
168+
169+
<a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Company</a>
170+
</div>
171+
<div role="none">
172+
<a href="#" class="block w-full px-5 py-3 text-center font-medium text-indigo-600 bg-gray-50 hover:bg-gray-100" role="menuitem">
173+
Log in
174+
</a>
175+
</div>
176+
</div>
177+
</div>
178+
</div>
179+
180+
<main class="mt-10 mx-auto max-w-7xl px-4 sm:mt-12 sm:px-6 md:mt-16 lg:mt-20 lg:px-8 xl:mt-28">
181+
<div class="sm:text-center lg:text-left">
182+
<h1 class="text-4xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl">
183+
<span class="block xl:inline">Data to enrich your</span>
184+
<span class="block text-indigo-600 xl:inline">online business</span>
185+
</h1>
186+
<p class="mt-3 text-base text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
187+
Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat aliqua.
188+
</p>
189+
<div class="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start">
190+
<div class="rounded-md shadow">
191+
<a href="#" class="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 md:py-4 md:text-lg md:px-10">
192+
Get started
193+
</a>
194+
</div>
195+
<div class="mt-3 sm:mt-0 sm:ml-3">
196+
<a href="#" class="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 md:py-4 md:text-lg md:px-10">
197+
Live demo
198+
</a>
199+
</div>
200+
</div>
201+
</div>
202+
</main>
203+
</div>
204+
</div>
205+
<div class="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
206+
<img class="h-56 w-full object-cover sm:h-72 md:h-96 lg:w-full lg:h-full" src="https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80" alt="">
207+
</div>
208+
</div>
209+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 translate-x-1/2">
2+
</div>

test/purger_test.rb

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
require "test_helper"
22

33
class Tailwindcss::PurgerTest < ActiveSupport::TestCase
4-
test "extract class names" do
5-
assert_equal %w[ div class max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 ].sort,
6-
%(<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">).scan(Tailwindcss::Purger::CLASS_NAME_PATTERN).flatten.sort
4+
test "extract class names from string" do
5+
assert_equal %w[ div class max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 translate-x-1/2 ].sort,
6+
Tailwindcss::Purger.extract_class_names(%(<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 translate-x-1/2">))
77
end
88

9-
test "basic purge" do
10-
purger = Tailwindcss::Purger.new(paths_with_css_class_names: [ "app/views/**/*.html*", "app/helpers/**/*.rb" ])
11-
purged = purger.purge(Pathname.new(__FILE__).join("../../app/assets/stylesheets/tailwind.css").read)
9+
test "extract class names from files" do
10+
assert_equal %w[ div class max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 translate-x-1/2 ].sort,
11+
Tailwindcss::Purger.extract_class_names_from(Rails.root.join("app/views/tailwinded/simple.html.erb"))
12+
end
1213

13-
assert_not purged =~ /.mt-6 \{/
14+
test "basic purge" do
15+
purged = Tailwindcss::Purger.purge \
16+
Pathname.new(__FILE__).join("../../app/assets/stylesheets/tailwind.css").read,
17+
keeping_class_names_from_files: Rails.root.glob("app/views/**/*.*") + Rails.root.glob("app/helpers/**/*.rb")
18+
19+
assert purged !~ /.mt-6 \{/
20+
21+
assert purged =~ /.mt-5 \{/
22+
assert purged =~ /.sm\\:px-6 \{/
23+
assert purged =~ /.translate-x-1\\\/2 \{/
1424
assert purged =~ /.mt-10 \{/
1525
end
1626
end

0 commit comments

Comments
 (0)