Skip to content

Commit 34d4910

Browse files
committed
feat: fall back to locally-installed tailwindcss executables
Closes #224
1 parent a4b2624 commit 34d4910

File tree

2 files changed

+80
-13
lines changed

2 files changed

+80
-13
lines changed

lib/tailwindcss/commands.rb

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,23 @@ def platform
1515
[:cpu, :os].map { |m| Gem::Platform.local.send(m) }.join("-")
1616
end
1717

18-
def executable(
19-
exe_path: File.expand_path(File.join(__dir__, "..", "..", "exe"))
20-
)
21-
if Tailwindcss::Upstream::NATIVE_PLATFORMS.keys.none? { |p| Gem::Platform.match(Gem::Platform.new(p)) }
22-
raise UnsupportedPlatformException, <<~MESSAGE
23-
tailwindcss-rails does not support the #{platform} platform
24-
Please install tailwindcss following instructions at https://tailwindcss.com/docs/installation
25-
MESSAGE
26-
end
27-
18+
def executable(exe_path: File.expand_path(File.join(__dir__, "..", "..", "exe")))
2819
exe_path = Dir.glob(File.expand_path(File.join(exe_path, "*", "tailwindcss"))).find do |f|
2920
Gem::Platform.match(Gem::Platform.new(File.basename(File.dirname(f))))
3021
end
3122

3223
if exe_path.nil?
24+
exe_path = find_executable_in_path("tailwindcss")
25+
end
26+
27+
if exe_path.nil?
28+
if Tailwindcss::Upstream::NATIVE_PLATFORMS.keys.none? { |p| Gem::Platform.match(Gem::Platform.new(p)) }
29+
raise UnsupportedPlatformException, <<~MESSAGE
30+
tailwindcss-rails does not support the #{platform} platform
31+
Please install tailwindcss following instructions at https://tailwindcss.com/docs/installation
32+
MESSAGE
33+
end
34+
3335
raise ExecutableNotFoundException, <<~MESSAGE
3436
Cannot find the tailwindcss executable for #{platform} in #{exe_path}
3537
@@ -72,6 +74,33 @@ def watch_command(poll: false, **kwargs)
7274
command << "-p" if poll
7375
end
7476
end
77+
78+
private
79+
80+
def find_executable_in_path(bin)
81+
# based on MakeMakefile.find_executable0
82+
executable_file = proc do |name|
83+
begin
84+
stat = File.stat(name)
85+
rescue SystemCallError
86+
else
87+
next name if stat.file? and stat.executable?
88+
end
89+
end
90+
91+
if path ||= ENV['PATH']
92+
path = path.split(File::PATH_SEPARATOR)
93+
else
94+
path = %w[/usr/local/bin /usr/ucb /usr/bin /bin]
95+
end
96+
97+
file = nil
98+
path.each do |dir|
99+
dir.sub!(/\A"(.*)"\z/m, '\1') if /mswin|mingw/ =~ RUBY_PLATFORM
100+
return file if executable_file.call(file = File.join(dir, bin))
101+
end
102+
nil
103+
end
75104
end
76105
end
77106
end

test/lib/tailwindcss/commands_test.rb

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
require "minitest/mock"
33

44
class Tailwindcss::CommandsTest < ActiveSupport::TestCase
5-
test ".platform is a string containing just the cpu and os (not the version)" do
6-
expected = "#{Gem::Platform.local.cpu}-#{Gem::Platform.local.os}"
7-
assert_equal(expected, Tailwindcss::Commands.platform)
5+
setup do
6+
@orig_path = ENV["PATH"]
7+
ENV["PATH"] = ""
8+
end
9+
10+
teardown do
11+
ENV["PATH"] = @orig_path
812
end
913

1014
def mock_exe_directory(platform)
@@ -18,6 +22,23 @@ def mock_exe_directory(platform)
1822
end
1923
end
2024

25+
def mock_path_binary
26+
Dir.mktmpdir do |dir|
27+
orig_path = ENV["PATH"]
28+
ENV["PATH"] = [dir, ENV["PATH"]].join(File::PATH_SEPARATOR)
29+
FileUtils.touch(File.join(dir, "tailwindcss"))
30+
FileUtils.chmod("ugo+x", File.join(dir, "tailwindcss"))
31+
yield(dir)
32+
ensure
33+
ENV["PATH"] = orig_path
34+
end
35+
end
36+
37+
test ".platform is a string containing just the cpu and os (not the version)" do
38+
expected = "#{Gem::Platform.local.cpu}-#{Gem::Platform.local.os}"
39+
assert_equal(expected, Tailwindcss::Commands.platform)
40+
end
41+
2142
test ".executable returns the absolute path to the binary" do
2243
mock_exe_directory("sparc-solaris2.8") do |dir, executable|
2344
expected = File.expand_path(File.join(dir, "sparc-solaris2.8", "tailwindcss"))
@@ -26,6 +47,23 @@ def mock_exe_directory(platform)
2647
end
2748
end
2849

50+
test "when a packaged exe is not found, .executable returns the absolute path to `tailwindcss` that is in PATH" do
51+
mock_path_binary do |installed|
52+
expected = File.join(installed, "tailwindcss")
53+
assert_equal(expected, Tailwindcss::Commands.executable(exe_path: "/blurgh"))
54+
end
55+
end
56+
57+
test "when a packaged exe is found, .executable ignores what's in PATH" do
58+
mock_exe_directory("sparc-solaris2.8") do |dir, executable|
59+
mock_path_binary do |installed|
60+
expected = File.expand_path(File.join(dir, "sparc-solaris2.8", "tailwindcss"))
61+
assert_equal(expected, executable, "assert on setup")
62+
assert_equal(expected, Tailwindcss::Commands.executable(exe_path: dir))
63+
end
64+
end
65+
end
66+
2967
test ".executable raises UnsupportedPlatformException when we're not on a supported platform" do
3068
Gem::Platform.stub(:match, false) do # nothing is supported
3169
assert_raises(Tailwindcss::Commands::UnsupportedPlatformException) do

0 commit comments

Comments
 (0)