rails / tailwindcss-rails

Other
1.39k stars 170 forks source link

Support @config directive and multiple *.tailwind.css files #303

Closed simmerz closed 8 months ago

simmerz commented 8 months ago

I noticed that #300 adds support for removing bin/dev and foreman, which is brilliant!

However, at present, tailwindcss-rails doesn't support multiple *tailwind.css files - the use case here is multiple different configs. I use it to set up a mailer css and a second for the default application.

Currently I have to patch in some additional commands and modify the Rake tasks as follows (I've taken this almost completely unchanged from #242):

# frozen_string_literal: true
# lib/tailwindcss/commands_ext.rb

module Tailwindcss
  # TailwindCSS Extension to allow multiple files to be compiled at once using the @config directive
  module CommandsExt
    def compile_file_command(file:, debug: false, **) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
      file = Pathname.new(file.to_s)
      input = file
      output = Rails.root.join('app/assets/builds', file.basename.sub('tailwind.', ''))
      config = input.open(&:readline).start_with?('@config') ? nil : Rails.root.join('config/tailwind.config.js')
      [
        executable(**),
        '-i', input.to_s,
        '-o', output.to_s
      ].tap do |command|
        command << '-c' if config
        command << config.to_s if config
        command << '--minify' unless debug
      end
    end

    def watch_file_command(poll: false, **)
      compile_file_command(**).tap do |command|
        command << '-w'
        command << '-p' if poll
      end
    end
  end
end

Tailwindcss::Commands.extend(Tailwindcss::CommandsExt)
# frozen_string_literal: true
# lib/tasks/tailwind.rake

require 'tailwindcss/commands_ext'

Rake::Task['tailwindcss:build'].clear
Rake::Task['tailwindcss:watch'].clear

namespace :tailwindcss do
  desc 'Build your Tailwind CSS'
  task :build do |_, args|
    debug = args.extras.include?('debug')

    files = Rails.root.join('app/assets/stylesheets').glob('*.tailwind.css')

    if files.count == 1 && files.first.basename == 'application.tailwind.css'
      command = Tailwindcss::Commands.compile_command(debug:)
      puts command.join(' ')
      system(*command, exception: true)
    else
      files.map do |file|
        Tailwindcss::Commands.compile_file_command(file:, debug:)
      end.each do |command| # rubocop:disable Style/MultilineBlockChain
        puts command.join(' ')
        system(*command, exception: true)
      end
    end
  end

  desc 'Watch and build your Tailwind CSS on file changes'
  task :watch do |_, args|
    debug = args.extras.include?('debug')
    poll = args.extras.include?('poll')

    files = Rails.root.join('app/assets/stylesheets').glob('*.tailwind.css')

    if files.count == 1 && files.first.basename == 'application.tailwind.css'
      command = Tailwindcss::Commands.watch_command(debug:, poll:)
      puts command.join(' ')
      system(*command)
    else
      trap('SIGINT') { exit }

      files.map do |file|
        Tailwindcss::Commands.watch_file_command(file:, debug:, poll:)
      end.each do |command| # rubocop:disable Style/MultilineBlockChain
        fork do
          puts command.join(' ')
          system(*command)
        end
      end
    end
  end
end

Rake::Task['assets:precompile'].enhance(['tailwindcss:build'])

if Rake::Task.task_defined?('test:prepare')
  Rake::Task['test:prepare'].enhance(['tailwindcss:build'])
elsif Rake::Task.task_defined?('db:test:prepare')
  Rake::Task['db:test:prepare'].enhance(['tailwindcss:build'])
end

My mailer.tailwind.css looks like:

@config '../../../config/tailwind.mailer.config.js';
@tailwind base;
@tailwind components;
@tailwind utilities;

and similarly, tailwind.tailwind.css:

@config '../../../config/tailwind.config.js';
@tailwind base;
@tailwind components;
@tailwind utilities;

With their appropriate configs as described.

I'm not sure what the best approach would be to get this functionality into the gem, but I'm certain it wouldn't be exactly as patched above, and for it to be viable, it would need to work in a way that the puma plugin could also support.

I'd be happy to have a go at a PR given some direction, but also welcome any discussion around this.

flavorjones commented 8 months ago

Thanks for opening this. Please jump in on #242 or #151