Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Engines pt.2
  • Loading branch information
bopm committed Apr 7, 2025
commit 5c88f6eb55c862988dd6969b448ea895d32c4cd0
48 changes: 27 additions & 21 deletions lib/tailwindcss/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
module Tailwindcss
module Commands
class << self
def compile_command(debug: false, **kwargs)
def rails_root
defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd)
end

def remove_tempfile!
@@tempfile.unlink if @@tempfile
end

def compile_command(input: application_css, debug: false, **kwargs)
debug = ENV["TAILWINDCSS_DEBUG"].present? if ENV.key?("TAILWINDCSS_DEBUG")
rails_root = defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd)

command = [
Tailwindcss::Ruby.executable(**kwargs),
"-i", rails_root.join("app/assets/tailwind/application.css").to_s,
"-i", application_css,
"-o", rails_root.join("app/assets/builds/tailwind.css").to_s,
]

Expand All @@ -21,6 +28,21 @@ def compile_command(debug: false, **kwargs)
command
end

def application_css
if engines_roots.any?
@@tempfile = Tempfile.new("tailwind.application.css")
engines_roots.each do |root|
@@tempfile.puts "@import \"#{root}\";"
end
@@tempfile.puts "\n@import \"#{rails_root.join('app/assets/tailwind/application.css')}\";"
@@tempfile.close

@@tempfile.path
else
rails_root.join("app/assets/tailwind/application.css").to_s
end
end

def watch_command(always: false, poll: false, **kwargs)
compile_command(**kwargs).tap do |command|
command << "-w"
Expand All @@ -39,7 +61,7 @@ def rails_css_compressor?
defined?(Rails) && Rails&.application&.config&.assets&.css_compressor.present?
end

def engines_tailwindcss_roots
def engines_roots
return [] unless defined?(Rails)

Rails::Engine.subclasses.select do |engine|
Expand All @@ -51,27 +73,11 @@ def engines_tailwindcss_roots
end
end.map do |engine|
[
Rails.root.join("app/assets/tailwind/#{engine.engine_name}/application.css"),
rails_root.join("app/assets/tailwind/#{engine.engine_name}/application.css"),
engine.root.join("app/assets/tailwind/#{engine.engine_name}/application.css")
].select(&:exist?).compact.first.to_s
end.compact
end

def enhance_command(command)
engine_roots = Tailwindcss::Commands.engines_tailwindcss_roots
if engine_roots.any?
Tempfile.create('tailwind.css') do |file|
file.write(engine_roots.map { |root| "@import \"#{root}\";" }.join("\n"))
file.write("\n@import \"#{Rails.root.join('app/assets/tailwind/application.css')}\";\n")
file.rewind
transformed_command = command.dup
transformed_command[2] = file.path
yield transformed_command if block_given?
end
else
yield command if block_given?
end
end
end
end
end
19 changes: 9 additions & 10 deletions lib/tasks/build.rake
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ namespace :tailwindcss do
verbose = args.extras.include?("verbose")

command = Tailwindcss::Commands.compile_command(debug: debug)
Tailwindcss::Commands.enhance_command(command) do |transformed_command|
env = Tailwindcss::Commands.command_env(verbose: verbose)
puts "Running: #{Shellwords.join(command)}" if verbose
env = Tailwindcss::Commands.command_env(verbose: verbose)
puts "Running: #{Shellwords.join(command)}" if verbose

system(env, *command, exception: true)
end
system(env, *command, exception: true)
remove_tempfile!
end

desc "Watch and build your Tailwind CSS on file changes"
Expand All @@ -21,14 +20,14 @@ namespace :tailwindcss do
verbose = args.extras.include?("verbose")

command = Tailwindcss::Commands.watch_command(always: always, debug: debug, poll: poll)
Tailwindcss::Commands.enhance_command(command) do |transformed_command|
env = Tailwindcss::Commands.command_env(verbose: verbose)
puts "Running: #{Shellwords.join(command)}" if verbose
env = Tailwindcss::Commands.command_env(verbose: verbose)
puts "Running: #{Shellwords.join(command)}" if verbose

system(env, *command)
end
system(env, *command)
rescue Interrupt
puts "Received interrupt, exiting tailwindcss:watch" if args.extras.include?("verbose")
ensure
remove_tempfile!
end
end

Expand Down
110 changes: 55 additions & 55 deletions test/lib/tailwindcss/commands_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ def setup
@executable = Tailwindcss::Ruby.executable
end

def teardown
super
if Tailwindcss::Commands.class_variable_defined?(:@@application_css)
Tailwindcss::Commands.remove_tempfile!
Tailwindcss::Commands.remove_class_variable(:@@application_css)
end
end

test ".compile_command" do
Rails.stub(:root, File) do # Rails.root won't work in this test suite
actual = Tailwindcss::Commands.compile_command
Expand Down Expand Up @@ -127,15 +135,15 @@ def setup
end
end

test ".engines_tailwindcss_roots when there are no engines" do
test ".engines_roots when there are no engines" do
Rails.stub(:root, Pathname.new("/dummy")) do
Rails::Engine.stub(:subclasses, []) do
assert_empty Tailwindcss::Commands.engines_tailwindcss_roots
assert_empty Tailwindcss::Commands.engines_roots
end
end
end

test ".engines_tailwindcss_roots when there are engines" do
test ".engines_roots when there are engines" do
Dir.mktmpdir do |tmpdir|
root = Pathname.new(tmpdir)

Expand Down Expand Up @@ -173,20 +181,14 @@ def setup
spec3.expect(:dependencies, [])

# Set up file structure
# Engine 1: CSS in engine root
engine1_css = engine_root1.join("app/assets/tailwind/test_engine1/application.css")
FileUtils.mkdir_p(File.dirname(engine1_css))
FileUtils.touch(engine1_css)

# Engine 2: CSS in Rails root
engine2_css = root.join("app/assets/tailwind/test_engine2/application.css")
FileUtils.mkdir_p(File.dirname(engine2_css))
FileUtils.touch(engine2_css)

# Engine 3: CsS in engine root, but no tailwindcss-rails dependency
engine3_css = engine_root2.join("app/assets/tailwind/test_engine3/application.css")
FileUtils.mkdir_p(File.dirname(engine3_css))
FileUtils.touch(engine3_css)

[engine1_css, engine2_css, engine3_css].each do |css_path|
FileUtils.mkdir_p(File.dirname(css_path))
FileUtils.touch(css_path)
end

find_by_name_results = {
"test_engine1" => spec1,
Expand All @@ -197,7 +199,7 @@ def setup
Gem::Specification.stub(:find_by_name, ->(name) { find_by_name_results[name] }) do
Rails.stub(:root, root) do
Rails::Engine.stub(:subclasses, [engine1, engine2]) do
roots = Tailwindcss::Commands.engines_tailwindcss_roots
roots = Tailwindcss::Commands.engines_roots

assert_equal 2, roots.size
assert_includes roots, engine1_css.to_s
Expand All @@ -212,63 +214,61 @@ def setup
end
end

test ".enhance_command when there are no engines" do
test ".application_css creates tempfile when engines exist" do
Dir.mktmpdir do |tmpdir|
root = Pathname.new(tmpdir)
input_path = root.join("app/assets/tailwind/application.css")
output_path = root.join("app/assets/builds/tailwind.css")

command = ["tailwindcss", "-i", input_path.to_s, "-o", output_path.to_s]
# Create necessary files
app_css = root.join("app/assets/tailwind/application.css")
FileUtils.mkdir_p(File.dirname(app_css))
FileUtils.touch(app_css)

engine_css = root.join("app/assets/tailwind/test_engine/application.css")
FileUtils.mkdir_p(File.dirname(engine_css))
FileUtils.touch(engine_css)

Rails.stub(:root, root) do
Tailwindcss::Commands.stub(:engines_tailwindcss_roots, []) do
Tailwindcss::Commands.enhance_command(command) do |actual|
assert_equal command, actual
end
Tailwindcss::Commands.stub(:engines_roots, [engine_css.to_s]) do
css_path = Tailwindcss::Commands.application_css
assert Tailwindcss::Commands.tempfile_path?(css_path)

content = File.read(css_path)
assert_match "@import \"#{engine_css}\";", content
assert_match "@import \"#{app_css}\";", content
end
end
end
end

test ".enhance_command when there are engines" do
test ".application_css uses application.css when no engines exist" do
Dir.mktmpdir do |tmpdir|
root = Pathname.new(tmpdir)
input_path = root.join("app/assets/tailwind/application.css")
output_path = root.join("app/assets/builds/tailwind.css")

# Create necessary files
FileUtils.mkdir_p(File.dirname(input_path))
FileUtils.touch(input_path)
app_css = root.join("app/assets/tailwind/application.css")
FileUtils.mkdir_p(File.dirname(app_css))
FileUtils.touch(app_css)

# Create engine CSS file
engine_css_path = root.join("app/assets/tailwind/test_engine/application.css")
FileUtils.mkdir_p(File.dirname(engine_css_path))
FileUtils.touch(engine_css_path)
Rails.stub(:root, root) do
Tailwindcss::Commands.stub(:engines_roots, []) do
css_path = Tailwindcss::Commands.application_css
assert_equal app_css.to_s, css_path
end
end
end
end

command = ["tailwindcss", "-i", input_path.to_s, "-o", output_path.to_s]
test ".remove_tempfile! cleans up temporary file" do
Dir.mktmpdir do |tmpdir|
root = Pathname.new(tmpdir)

Rails.stub(:root, root) do
Tailwindcss::Commands.stub(:engines_tailwindcss_roots, [engine_css_path.to_s]) do
Tailwindcss::Commands.enhance_command(command) do |actual|
# Command should be modified to use a temporary file
assert_equal command[0], actual[0] # executable
assert_equal command[1], actual[1] # -i flag
assert_equal command[3], actual[3] # -o flag
assert_equal command[4], actual[4] # output path

temp_path = Pathname.new(actual[2])
refute_equal command[2], temp_path.to_s # input path should be different
assert_match(/tailwind\.css/, temp_path.basename.to_s) # should use temp file
assert_includes [Dir.tmpdir, '/tmp'], temp_path.dirname.to_s # should be in temp directory

# Check temp file contents
temp_content = File.read(temp_path)
expected_content = <<~CSS
@import "#{engine_css_path}";
@import "#{input_path}";
CSS
assert_equal expected_content.strip, temp_content.strip
end
Tailwindcss::Commands.stub(:engines_roots, ["dummy_engine"]) do
css_path = Tailwindcss::Commands.application_css
assert File.exist?(css_path)

debugger
Tailwindcss::Commands.remove_tempfile!
refute File.exist?(css_path)
end
end
end
Expand Down