Skip to content

Instantly share code, notes, and snippets.

@jeromecornet
Last active August 26, 2021 21:14
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeromecornet/93f04cc8358dcf3a96750615062bfc70 to your computer and use it in GitHub Desktop.
Save jeromecornet/93f04cc8358dcf3a96750615062bfc70 to your computer and use it in GitHub Desktop.
Tailwindcss with sprockets
tailwind_transformer.rb -> app/models/tailwind_transformer.rb
tailwind.rb -> config/initializers/tailwind.rb
tailwind.config.js.erb -> config/tailwind.config.js.erb
package.json -> ./package.json
{
...
"dependencies": {
"@tailwindcss/aspect-ratio": "^0.2.1",
"@tailwindcss/forms": "^0.3.3",
"@tailwindcss/typography": "^0.4.1",
"autoprefixer": "^10.0.2",
"clean-css-cli": "^5.3.3",
"postcss": "8.3.6",
"tailwindcss": "^2.2.7",
"tailwindcss-cli": "^0.1.2"
}
}
module.exports = {
mode: 'jit',
darkMode: 'class',
purge: {
content: [
'./app/**/*.erb',
'./app/helpers/**/*.rb',
'./app/**/*.html',
'./app/**/*.js',
],
css: [
'./app/**/*.scss',
'./app/**/*.css',
]
},
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
}
},
},
variants: {},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}
Rails.application.reloader.to_prepare do
Sprockets.register_compressor "text/css", :tailwind, TailwindTransformer
Rails.application.config.assets.css_compressor = :tailwind
end
if Rails.env.production?
Rails.application.config.assets.configure do |env|
env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end
end
# frozen_string_literal: true
require "open3"
class TailwindTransformer
def self.instance
@instance ||= new
end
def self.call(input)
instance.call(input)
end
def temp_config
Rails.root.join("tmp", "tailwind.config.js")
end
def initialize(options = {})
templated = Rails.root.join("config", "tailwind.config.js.erb")
non_templated = Rails.root.join("config", "tailwind.config.js")
if File.exist?(templated)
File.write(temp_config, ERB.new(File.read(templated)).result)
elsif File.exist?(non_templated)
FileUtils.cp(non_templated, temp_config)
else
raise StandardError, "Cannot find config/tailwind.config.js[.erb]"
end
@options = {
files: ["application"],
}.merge(options).freeze
end
def call(input)
if input[:name].in?(@options[:files])
temp_file = Tempfile.new([input[:name], ".css"])
temp_file.open
temp_file.write(input[:data])
temp_file.flush
out, err, status = Open3.capture3("npx", "tailwindcss-cli", "build", "-i", temp_file.path, "--config", temp_config.to_s)
unless status.success?
raise StandardError,
"Error while compiling #{input[:filename]}. " \
"npx tailwindcss-cli build '#{temp_file.path}' --config '#{temp_config}' returned: \n#{err}"
end
return { data: out } unless ENV["ASSETS_MODE"] == "production" || Rails.env.production?
File.truncate(temp_file.path, 0)
temp_file.open
temp_file.write(out)
temp_file.flush
out, err, status = Open3.capture3("npx", "cleancss", temp_file.path)
unless status.success?
raise StandardError,
"Error while compiling #{input[:filename]}. " \
"NODE_ENV=production npx tailwindcss-cli build '#{temp_file.path}' --config '#{temp_config}' returned: \n#{err}"
end
{ data: out }
else
input[:data]
end
end
end
@travisp
Copy link

travisp commented Apr 24, 2021

Line 22 of the transformer has a type exists instead of exist? or exists?

This only worked for me when I moved registering the compressor and setting it out of Rails.application.reloader.to_prepare and to the bottom of the assets.rb file. I think there was an issue with load order around setting precompiled assets, but I'm not certain.

@jeromecornet
Copy link
Author

Just pushed an update so the css is also processed with cleancss in production

@domchristie
Copy link

Just pushed an update so the css is also processed with cleancss in production

Could/should this be handled by a Sprockets compressor?

@jeromecornet
Copy link
Author

Just pushed an update so the css is also processed with cleancss in production

Could/should this be handled by a Sprockets compressor?

Probably. You can easily remove the second stage after https://gist.github.com/jeromecornet/93f04cc8358dcf3a96750615062bfc70#file-tailwind_transformer-rb-L47 and simply return the output of the first stage.

I just whipped this up because I wanted the combination of tailwindcss with its @apply directives in a rails app without webpacker (using importmaps, now https://github.com/rails/importmap-rails) and https://github.com/rails/tailwindcss-rails takes a fully no javascript approach (packaging the full css, then purging it with a ruby-based class detector). I didn't try to understand anything about sprockets when I did this (hence it's just a gist, not a proper project). I would love for someone to package up properly something like this though 😄

@domchristie
Copy link

I just whipped this up because I wanted the combination of tailwindcss … without webpacker

Same! I created a demo here: https://github.com/domchristie/tailwindcss-jit-rails but it's much less integrated with Sprockets, and much more manual. I don't have experience authoring gems or Sprockets extenions, but would also love to see this approach packaged up :)

This looks like a great introduction to integrating a node CLI with Sprockets. Thanks for sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment