Skip to content

Commit ab00b7c

Browse files
flavorjonesDavid Heinemeier Hansson
and
David Heinemeier Hansson
authored
Package the tailwindcss binary executable (#96)
* feat: package tailwindcss binary executables with the gem See rakelib/package.rake for details. * ci: verify that the tailwindcss binaries work as expected * provide license for redistributed software * Ignore exe properly * Drop old machinery * Switch to generators and bundling style production * Do initial run on install * Update README.md * Latest version Co-authored-by: David Heinemeier Hansson <[email protected]>
1 parent 777e1d2 commit ab00b7c

35 files changed

+350
-1910
lines changed

.github/workflows/gem-install.yml

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Native Gems
2+
on: [push, pull_request]
3+
jobs:
4+
package:
5+
strategy:
6+
fail-fast: false
7+
matrix:
8+
platform: ["x64-mingw32", "x86_64-darwin", "x86_64-linux"]
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- uses: ruby/setup-ruby@v1
13+
with:
14+
ruby-version: "3.0"
15+
bundler: latest
16+
bundler-cache: true
17+
- run: "bundle exec rake gem:${{matrix.platform}}"
18+
- uses: actions/upload-artifact@v2
19+
with:
20+
name: gem-${{matrix.platform}}
21+
path: pkg
22+
retention-days: 1
23+
24+
linux-install:
25+
needs: ["package"]
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: ruby/setup-ruby@v1
29+
with:
30+
ruby-version: "3.0"
31+
- uses: actions/download-artifact@v2
32+
with:
33+
name: gem-x86_64-linux
34+
path: pkg
35+
- run: "gem install pkg/tailwindcss-rails-*.gem"
36+
- run: "tailwindcss --help"
37+
38+
darwin-install:
39+
needs: ["package"]
40+
runs-on: macos-latest
41+
steps:
42+
- uses: ruby/setup-ruby@v1
43+
with:
44+
ruby-version: "3.0"
45+
- uses: actions/download-artifact@v2
46+
with:
47+
name: gem-x86_64-darwin
48+
path: pkg
49+
- run: "gem install pkg/tailwindcss-rails-*.gem"
50+
- run: "tailwindcss --help"
51+
52+
windows-install:
53+
needs: ["package"]
54+
runs-on: windows-latest
55+
steps:
56+
- uses: ruby/setup-ruby@v1
57+
with:
58+
ruby-version: "3.0"
59+
- uses: actions/download-artifact@v2
60+
with:
61+
name: gem-x64-mingw32
62+
path: pkg
63+
- run: "gem install pkg/tailwindcss-rails-*.gem"
64+
- run: "tailwindcss --help"

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
.byebug_history
1313
*.gem
1414
.idea/
15-
**/tmp/
15+
**/tmp/
16+
/exe/*/tailwindcss

Gemfile.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
tailwindcss-rails (1.0.0)
4+
tailwindcss-rails (2.0.0)
55
railties (>= 6.0.0)
66

77
GEM

LICENSE-DEPENDENCIES

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
tailwindcss-rails may redistribute executables from the https://github.com/tailwindlabs/tailwindcss project
2+
3+
The license for that software can be found at https://github.com/tailwindlabs/tailwindcss/blob/master/LICENSE which is reproduced here for your convenience:
4+
5+
MIT License
6+
7+
Copyright (c) Adam Wathan <[email protected]>
8+
Copyright (c) Jonathan Reinink <[email protected]>
9+
10+
Permission is hereby granted, free of charge, to any person obtaining a copy
11+
of this software and associated documentation files (the "Software"), to deal
12+
in the Software without restriction, including without limitation the rights
13+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
copies of the Software, and to permit persons to whom the Software is
15+
furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included in all
18+
copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
SOFTWARE.

README.md

+11-48
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,28 @@
22

33
[Tailwind CSS](https://tailwindcss.com) is a utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.
44

5-
This gem gives access to the standard Tailwind CSS framework configured for dark mode, forms, aspect-ratio, typography, and the Inter font via the asset pipeline using Sprockets (and soon [Propshaft](https://github.com/rails/propshaft)).
5+
This gem wraps [the standalone executable version](https://tailwindcss.com/blog/standalone-cli) of the Tailwind CSS 3 framework. These executables are platform specific, but the correct gem will automatically be picked for your platform. Supported platforms are Linux x64, macOS arm64, macOS x64, and Windows x64.
66

7-
If you need to customize Tailwind, you will need to install it under a full JavaScript bundling setup, such as [cssbundling-rails](https://github.com/rails/cssbundling-rails). This gem was specifically designed not to require a Node.js environment. If you're already using such an environment, you won't need this gem.
7+
You can customize the Tailwind build through the `config/tailwind.config.js` file, just like you would if Tailwind was running in a traditional node installation. All the first-party plugins are supported.
88

9-
Production-mode purging of unused css class names is provided by a Sprockets compressor built into this gem. This compressor ensures that only the css classes used by files in `app/views` and `app/helpers` are included. In development mode, the full 7mb+ Tailwind stylesheet is loaded.
9+
The installer will create your Tailwind input file in `app/assets/stylesheets/application.tailwind.css`. This is where you import the plugins you want to use, and where you can setup your custom `@apply` rules. When you run `rails tailwindcss:build`, this input file will be used to generate the output in `app/assets/builds/tailwind.css`. That's the output CSS that you'll include in your app (the installer automatically configures this, alongside the Inter font as well).
1010

11+
If you need to use a custom input or output file, you can run `bundle exec tailwindcss` to access the platform-specific executable, and give it your own build options.
1112

12-
## Installation
13-
14-
1. Run `./bin/bundle add tailwindcss-rails`
15-
2. Run `./bin/rails tailwindcss:install`
16-
17-
The last step adds the purger compressor to `config/environments/production.rb`. This ensures that when `assets:precompile` is called during deployment that the unused class names are not included in the tailwind output css used by the app. It also adds a `stylesheet_link_tag "tailwind"` and `stylesheet_link_tag "inter-font"` to your `app/views/layouts/application.html.erb` file.
18-
19-
You can do these things yourself, if you've changed the default setup.
20-
21-
22-
## Purging in production
23-
24-
The Tailwind CSS framework starts out as a massive file, which gives you all the combinations of utility classes for development, but you wouldn't want to ship all those unused classes in production. So the Sprockets compressor included in this gem is used to purge the tailwind file from all those unused classes for production.
25-
26-
Note: This compressor is currently not compatible with the default Sprockets cache system due to the fact its output depends on files outside of Sprockets (all the files observed for utility class name usage), so this cache is disabled in production. If you need to disable it in other deployed environments, add the following to that environment configuration file:
13+
When you're developing your application, you want to run Tailwind in watch mode, so changes are automatically reflected in the generated CSS output. You can do this either by running `rails tailwindcss:watch` as a separate process, or by running `./bin/dev` which uses [foreman](https://github.com/ddollar/foreman) to starts both the Tailwind watch process and the rails server in development mode.
2714

28-
```ruby
29-
Rails.application.config.assets.configure do |env|
30-
env.cache = ActiveSupport::Cache.lookup_store(:null_store)
31-
end
32-
```
3315

16+
## Installation
3417

35-
## Configuration
36-
37-
If you need to customize what files are searched for class names when using the asset pipeline, you need to replace the compressor line with something like:
38-
39-
```ruby
40-
config.assets.css_compressor = Tailwindcss::Compressor.new(files_with_class_names: Rails.root.glob("app/somewhere/**/*.*"))
41-
```
42-
43-
By default, the CSS purger will only operate on the tailwind css file included with this gem. If you want to use it more broadly:
44-
45-
```ruby
46-
config.assets.css_compressor = Tailwindcss::Compressor.new(only_purge: %w[ tailwind and_my_other_css_file ])
47-
```
48-
49-
50-
## Tailwind versions
51-
52-
The Tailwind CSS main file that's being used before purging consists of these versions:
18+
With Rails 7 you can generate a new application preconfigured with Tailwind by using `--css tailwind`. If you're adding Tailwind later, you need to:
5319

54-
* @tailwindcss/aspect-ratio 0.2.1
55-
* @tailwindcss/forms 0.3.3
56-
* @tailwindcss/typography 0.4.1
57-
* autoprefixer 10.3.1
58-
* tailwindcss 2.2.15
20+
1. Run `./bin/bundle add tailwindcss-rails`
21+
2. Run `./bin/rails tailwindcss:install`
5922

6023

61-
## Compatibility with Tailwind 3.0
24+
## Building in production
6225

63-
This gem is not yet compatible with the JIT approach taken with Tailwind 3.0. We're working with the team on an approach that would bring compatibility, but at the moment you'd need to use [cssbundling-rails](https://github.com/rails/cssbundling-rails/) (and thus bring Node into your app) in order to use 3.0.
26+
The `tailwindcss:build` is automatically attached to `assets:precompile`, so before the asset pipeline digests the files, the Tailwind output will be generated.
6427

6528

6629
## Conflict with sassc-rails

bin/release

+6-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ git commit -m "Bump version for $VERSION"
1414
git push
1515
git tag v$VERSION
1616
git push --tags
17-
gem build tailwindcss-rails.gemspec
18-
gem push "tailwindcss-rails-$VERSION.gem" --host https://rubygems.org
19-
rm "tailwindcss-rails-$VERSION.gem"
17+
18+
rake package
19+
for gem in pkg/tailwindcss-rails-$VERSION*.gem ; do
20+
gem push "$gem" --host https://rubygems.org
21+
rm "$gem"
22+
done

exe/tailwindcss

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#! /usr/bin/env ruby
2+
# because rubygems shims assume a gem's executables are Ruby
3+
4+
require "shellwords"
5+
6+
platform_dir = Dir.glob(File.join(__dir__, "*")).select do |f|
7+
File.directory?(f) && Gem::Platform.match(File.basename(f))
8+
end.first
9+
if platform_dir.nil?
10+
raise "Cannot find the tailwindcss executable in #{__dir__} (1)"
11+
end
12+
13+
exe_path = File.join(platform_dir, "tailwindcss")
14+
if !File.exist?(exe_path)
15+
raise "Cannot find the tailwindcss executable in #{__dir__} (2)"
16+
end
17+
18+
command = Shellwords.join([exe_path, ARGV].flatten)
19+
puts "+ #{command}"
20+
exec(command)

lib/install/Procfile.dev

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
web: bin/rails server -p 3000
2+
css: rails tailwindcss:watch

lib/install/application.tailwind.css

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
/*
6+
7+
@layer components {
8+
.btn-primary {
9+
@apply py-2 px-4 bg-blue-200;
10+
}
11+
}
12+
13+
*/

lib/install/dev

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
if ! command -v foreman &> /dev/null
4+
then
5+
echo "Installing foreman..."
6+
gem install foreman
7+
fi
8+
9+
foreman start -f Procfile.dev

tailwind.config.js renamed to lib/install/tailwind.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ const defaultTheme = require('tailwindcss/defaultTheme')
22

33
module.exports = {
44
darkMode: 'media',
5+
content: [
6+
'./app/helpers/**/*.rb',
7+
'./app/javascript/**/*.js',
8+
'./app/views/**/*'
9+
],
510
theme: {
611
extend: {
712
fontFamily: {

lib/install/tailwindcss.rb

+38-8
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,50 @@
33
if APPLICATION_LAYOUT_PATH.exist?
44
say "Add Tailwindcss include tags and container element in application layout"
55
insert_into_file APPLICATION_LAYOUT_PATH.to_s, <<~ERB.indent(4), before: /^\s*<%= stylesheet_link_tag/
6-
<%= stylesheet_link_tag "inter-font", "data-turbo-track": "reload" %>
7-
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
6+
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
87
ERB
98
insert_into_file APPLICATION_LAYOUT_PATH.to_s, %( <main class="container mx-auto mt-28 px-5 flex">\n ), before: /^\s*<%= yield/
109
insert_into_file APPLICATION_LAYOUT_PATH.to_s, %(\n </main>), after: /^\s*<%= yield %>/
1110
else
1211
say "Default application.html.erb is missing!", :red
13-
say %( Add <%= stylesheet_link_tag "inter-font", "data-turbo-track": "reload" %> and <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %> within the <head> tag in your custom layout.)
12+
say %( Add <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> within the <head> tag in your custom layout.)
1413
end
1514

16-
# No longer included by default in Rails 7, but for earlier versions of Rails
17-
if (scaffolds_css_path = Rails.root.join("app/assets/stylesheets/scaffolds.scss")).exist?
18-
remove_file scaffolds_css_path
15+
say "Build into app/assets/builds"
16+
empty_directory "app/assets/builds"
17+
keep_file "app/assets/builds"
18+
19+
if (sprockets_manifest_path = Rails.root.join("app/assets/config/manifest.js")).exist?
20+
append_to_file sprockets_manifest_path, %(//= link_tree ../builds\n)
21+
end
22+
23+
if Rails.root.join(".gitignore").exist?
24+
append_to_file(".gitignore", %(\n/app/assets/builds/*\n!/app/assets/builds/.keep\n))
25+
end
26+
27+
unless Rails.root.join("config/tailwind.config.js").exist?
28+
say "Add default config/tailwindcss.config.js"
29+
copy_file "#{__dir__}/tailwind.config.js", "config/tailwind.config.js"
30+
end
31+
32+
unless Rails.root.join("app/assets/stylesheets/application.tailwind.css").exist?
33+
say "Add default app/assets/stylesheets/application.tailwind.css"
34+
copy_file "#{__dir__}/application.tailwind.css", "app/assets/stylesheets/application.tailwind.css"
35+
end
36+
37+
if Rails.root.join("Procfile.dev").exist?
38+
append_to_file "Procfile.dev", "css: rails tailwindcss:watch\n"
39+
else
40+
say "Add default Procfile.dev"
41+
copy_file "#{__dir__}/Procfile.dev", "Procfile.dev"
42+
43+
say "Ensure foreman is installed"
44+
run "gem install foreman"
1945
end
2046

21-
say "Turn on purging of unused css classes in production"
22-
gsub_file Rails.root.join("config/environments/production.rb"), /^\s+#?\s+config.assets.css_compressor =.*$/, %( config.assets.css_compressor = :purger)
47+
say "Add bin/dev to start foreman"
48+
copy_file "#{__dir__}/dev", "bin/dev"
49+
chmod "bin/dev", 0755, verbose: false
50+
51+
say "Compile initial Tailwind build"
52+
run "rails tailwindcss:build"

lib/tailwindcss/compressor.rb

-33
This file was deleted.

lib/tailwindcss/engine.rb

+1-12
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
require "rails"
2-
require "tailwindcss/compressor"
32

43
module Tailwindcss
54
class Engine < ::Rails::Engine
6-
initializer "tailwindcss.compressor" do
7-
Sprockets.register_compressor "text/css", :purger, Tailwindcss::Compressor
8-
end
9-
105
initializer "tailwindcss.assets" do
11-
Rails.application.config.assets.precompile += %w( tailwind.css inter-font.css )
6+
Rails.application.config.assets.precompile += %w( inter-font.css )
127
end
138

149
initializer "tailwindcss.disable_generator_stylesheets" do
1510
Rails.application.config.generators.stylesheets = false
1611
end
1712

18-
initializer "tailwindcss.disable_assets_cache" do
19-
Rails.application.config.assets.configure do |env|
20-
env.cache = ActiveSupport::Cache.lookup_store(:null_store)
21-
end if Rails.env.production?
22-
end
23-
2413
config.app_generators do |g|
2514
g.template_engine :tailwindcss
2615
end

0 commit comments

Comments
 (0)