如何在 Rails 7 引擎中使用 Tailwind CSS gem?

jam*_*esc 8 ruby-on-rails rails-engines tailwind-css ruby-on-rails-7

如何在轨道发动机中使用顺风?根据文档,向 Rails 生成器提供 css 参数应该可以工作

使用 Rails 7.0.2.2 引擎生成

rails plugin new tailtest --mountable --full -d postgresql --css tailwind
Run Code Online (Sandbox Code Playgroud)

这会使用 Postgresql 生成引擎,但对 tailwind 根本不起作用,并且遵循手动安装说明也会失败。

根据文档运行,bundle add tailwindcss-rails将 tailwind 添加到 gemfile 而不是引擎 tailtest.gemspec 因此,在将依赖项添加到 gemspec 之后

spec.add_dependency "tailwindcss-rails", "~> 2.0"
Run Code Online (Sandbox Code Playgroud)

并且运行bundle install确实安装了引擎,但是其余的手动安装失败

然后将 require 添加到 lib/engine.rb

require "tailwindcss-rails"
module Tailtest
  class Engine < ::Rails::Engine
    isolate_namespace Tailtest
  end
end
Run Code Online (Sandbox Code Playgroud)

然后运行安装过程失败

rails tailwindcss:install
Resolving dependencies...
rails aborted!
Don't know how to build task 'tailwindcss:install' (See the list of available tasks with `rails --tasks`)
Did you mean?  app:tailwindcss:install
Run Code Online (Sandbox Code Playgroud)

显然该app:tailwindcss:install命令也失败了。

所以我可能在 engine.rb 文件中缺少某种初始化程序,但不知道它应该是什么。

Ale*_*lex 12

这与How to set up importmap-rails in Rails 7 engine? 的想法相同。。我们不需要使用安装任务。即使您能够运行它,它对引擎也没有帮助(请参阅答案末尾的解释)。

rails plugin new没有--css选项。要查看可用选项:rails plugin new -h

更新引擎的 gemspec 文件:

# my_engine/my_engine.gemspec

spec.add_dependency "tailwindcss-rails"
Run Code Online (Sandbox Code Playgroud)

更新engine.rb

# my_engine/lib/my_engine/engine.rb

module MyEngine
  class Engine < ::Rails::Engine
    isolate_namespace MyEngine

    # NOTE: add engine manifest to precompile assets in production, if you don't have this yet.
    initializer "my-engine.assets" do |app|
      app.config.assets.precompile += %w[my_engine_manifest]
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

更新资产清单:

# my_engine/my_engine.gemspec

spec.add_dependency "tailwindcss-rails"
Run Code Online (Sandbox Code Playgroud)

更新引擎的布局:

# my_engine/lib/my_engine/engine.rb

module MyEngine
  class Engine < ::Rails::Engine
    isolate_namespace MyEngine

    # NOTE: add engine manifest to precompile assets in production, if you don't have this yet.
    initializer "my-engine.assets" do |app|
      app.config.assets.precompile += %w[my_engine_manifest]
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

bundle show命令将为我们提供 gem 安装的路径,因此我们可以复制一些文件:

# my_engine/app/assets/config/my_engine_manifest.js

//= link_tree ../builds/ .css
Run Code Online (Sandbox Code Playgroud)

从以下位置复制tailwind.config.js文件tailwindcss-rails

# my_engine/app/views/layouts/my_engine/application.html.erb

<!DOCTYPE html>
<html>
  <head>
   <%# 
       NOTE: make sure this name doesn't clash with anything in the main app.
             think of it as `require` and `$LOAD_PATH`,
             but instead it is `stylesheet_link_tag` and `manifest.js`.
    %>
    <%= stylesheet_link_tag "my_engine", "data-turbo-track": "reload" %>
  </head>
  <body> <%= yield %> </body>
</html>
Run Code Online (Sandbox Code Playgroud)

将application.tailwind.css文件复制到任何目录以适合您的设置:

$ bundle show tailwindcss-rails
/home/alex/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/tailwindcss-rails-2.0.8-x86_64-linux
Run Code Online (Sandbox Code Playgroud)

因为tailwindcss-rails使用独立的可执行文件,所以我们不需要节点或rails来编译样式表。我们只需要获取可执行文件本身。

可执行文件位于此处https://github.com/rails/tailwindcss-rails/tree/v2.0.8/exe/。我们可以直接调用可执行文件,而不是运行构建任务https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake 。

$ cp $(bundle show tailwindcss-rails)/lib/install/tailwind.config.js config/tailwind.config.js
Run Code Online (Sandbox Code Playgroud)

使用-w选项启动观看模式。

$ cp $(bundle show tailwindcss-rails)/lib/install/application.tailwind.css app/assets/stylesheets/application.tailwind.css
Run Code Online (Sandbox Code Playgroud)

输出文件应与 中的名称匹配stylesheet_link_tag "my_engine"

现在您已经有了一个普通的my_engine.css文件,可以用它做您想做的事情。在布局中使用它,从主应用程序application.css中需要它。通常的铁路资产管道规则适用。

如果您想将所有这些放入任务中,请使用Engine.root来获取路径。

# my_engine/lib/tasks/my_engine.rake

task :tailwind_engine_watch do
  require "tailwindcss-rails"
  # NOTE: tailwindcss-rails is an engine
  system "#{Tailwindcss::Engine.root.join("exe/tailwindcss")} \
         -i #{MyEngine::Engine.root.join("app/assets/stylesheets/application.tailwind.css")} \
         -o #{MyEngine::Engine.root.join("app/assets/builds/my_engine.css")} \
         -c #{MyEngine::Engine.root.join("config/tailwind.config.js")} \
         --minify -w"
end
Run Code Online (Sandbox Code Playgroud)

从引擎目录:

$ $(bundle show tailwindcss-rails)/exe/tailwindcss -i app/assets/stylesheets/application.tailwind.css -o app/assets/builds/my_engine.css -c config/tailwind.config.js --minify
Run Code Online (Sandbox Code Playgroud)

如果您有很多引擎需要设置,请创建自己的安装任务:

desc "Install tailwindcss into our engine"
task :tailwind_engine_install do
  require "tailwindcss-rails"

  # NOTE: use default app template, which will fail to modify layout, manifest,
  #       and the last command that compiles the initial `tailwind.css`.
  #       It will also add `bin/dev` and `Procfile.dev` which we don't need.
  #       Basically, it's useless in the engine as it is.
  template = Tailwindcss::Engine.root.join("lib/install/tailwindcss.rb")

  # TODO: better to copy the template from 
  #       https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/install/tailwindcss.rb
  #       and customize it
  # template = MyEngine::Engine.root("lib/install/tailwindcss.rb")

  require "rails/generators"
  require "rails/generators/rails/app/app_generator"
  
  # NOTE: because the app template uses `Rails.root` it will run the install
  #       on our engine's dummy app. Just override `Rails.root` with our engine
  #       root to run install in the engine directory.
  Rails.configuration.root = MyEngine::Engine.root

  generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root }
  generator.apply template
end
Run Code Online (Sandbox Code Playgroud)

安装任务参考:
https://github.com/rails/rails/blob/v7.0.2.4/railties/lib/rails/tasks/framework.rake#L8
https://github.com/rails/tailwindcss-rails /blob/v2.0.8/lib/tasks/install.rake

观看任务参考:
https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake#L10


更新如何合并两个顺风。

上面的设置假设引擎是它自己独立的东西,就像管理后端一样,它有自己的路由、模板和样式。如果引擎功能要与主应用程序混合,例如view_component集合,则 tailwind 样式将相互覆盖。在这种情况下,可以使用前缀隔离引擎样式: https:
//tailwindcss.com/docs/configuration#prefix

Tailwind 样式不能混合的原因是大多数选择器具有相同的特异性,并且顺序非常重要。

这是一个例子。带引擎的主应用程序,均使用 tailwind,均单独编译样式,tailwind 配置仅监视引擎中的一个文件和主应用程序中的一个文件,仅使用@tailwind 实用程序;指示:

我们想要在主应用程序中使用的引擎模板应该可以正常工作:

$ $(bundle show tailwindcss-rails)/exe/tailwindcss -i app/assets/stylesheets/application.tailwind.css -o app/assets/builds/my_engine.css -c config/tailwind.config.js --minify -w
Run Code Online (Sandbox Code Playgroud)

但当在主应用程序中渲染时,它永远不会变成蓝色。这是演示设置:

# my_engine/lib/tasks/my_engine.rake

task :tailwind_engine_watch do
  require "tailwindcss-rails"
  # NOTE: tailwindcss-rails is an engine
  system "#{Tailwindcss::Engine.root.join("exe/tailwindcss")} \
         -i #{MyEngine::Engine.root.join("app/assets/stylesheets/application.tailwind.css")} \
         -o #{MyEngine::Engine.root.join("app/assets/builds/my_engine.css")} \
         -c #{MyEngine::Engine.root.join("config/tailwind.config.js")} \
         --minify -w"
end
Run Code Online (Sandbox Code Playgroud)

它看起来像这样:

$ bin/rails app:tailwind_engine_watch
+ /home/alex/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/tailwindcss-rails-2.0.8-x86_64-linux/exe/x86_64-linux/tailwindcss -i /home/alex/code/stackoverflow/my_engine/app/assets/stylesheets/application.tailwind.css -o /home/alex/code/stackoverflow/my_engine/app/assets/builds/my_engine.css -c /home/alex/code/stackoverflow/my_engine/config/tailwind.config.js --minify -w

Rebuilding...
Done in 549ms.
Run Code Online (Sandbox Code Playgroud)
desc "Install tailwindcss into our engine"
task :tailwind_engine_install do
  require "tailwindcss-rails"

  # NOTE: use default app template, which will fail to modify layout, manifest,
  #       and the last command that compiles the initial `tailwind.css`.
  #       It will also add `bin/dev` and `Procfile.dev` which we don't need.
  #       Basically, it's useless in the engine as it is.
  template = Tailwindcss::Engine.root.join("lib/install/tailwindcss.rb")

  # TODO: better to copy the template from 
  #       https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/install/tailwindcss.rb
  #       and customize it
  # template = MyEngine::Engine.root("lib/install/tailwindcss.rb")

  require "rails/generators"
  require "rails/generators/rails/app/app_generator"
  
  # NOTE: because the app template uses `Rails.root` it will run the install
  #       on our engine's dummy app. Just override `Rails.root` with our engine
  #       root to run install in the engine directory.
  Rails.configuration.root = MyEngine::Engine.root

  generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root }
  generator.apply template
end
Run Code Online (Sandbox Code Playgroud)

^ 您可以点击“运行”并单击“整页”。主应用bg-red-500程序选择器位于最后,因此它会覆盖引擎sm:bg-blue-500选择器,媒体查询不会添加到特异性得分。mt-1这与您无法覆盖(例如,使用 )的原因相同,m-2margin top 出现在样式表的后面。这就是@layer指令很重要的原因。

解决这个问题的唯一方法是在主应用程序中运行 tailwind 时观察引擎目录,以便样式以正确的顺序一起编译。这意味着您实际上并不需要发动机顺风:

module.exports = {
  content: [
    "./app/**/*",
    "/just/type/the/path/to/engine/views",
    "/or/see/updated/task/below",
  ],
}
Run Code Online (Sandbox Code Playgroud)

我尝试过的其他方法,例如为主应用程序和引擎的每一层运行 6 个 tailwind 命令,这样我就可以将它们按顺序排列,效果更好,但仍然有点乱序并且重复。或者做一个@import并以某种方式让postcss-import知道在哪里寻找引擎样式(我不知道,我只是将它符号链接到node_modules中进行测试),但这仍然需要顺风来观察引擎文件。


我做了更多挖掘,tailwind cli 有一个--content选项,它将content覆盖tailwind.config.js。我们可以用它来设置一个新任务:

<!-- blep/app/views/blep/_partial.html.erb -->

<div class="bg-red-500 sm:bg-blue-500"> red never-blue </div>
Run Code Online (Sandbox Code Playgroud)

https://github.com/rails/tailwindcss-rails/blob/v2.0.21/lib/tasks/build.rake#L11


小智 6

亚历克斯的回答非常好,我希望我在开始时就拥有它。(但我什至没有向谷歌提出问题)只想添加两件事:

1-一个小的简化。我刚刚制作了一个脚本来在引擎中运行顺风

#!/usr/bin/env sh
# Since tailwind does not install into the engine, this will
# watch and recompile during development
# tailwindcss executable must exist (by bundling tailwindcss-rails eg)

tailwindcss -i app/assets/stylesheets/my_engine.tailwind.css \
        -o app/assets/stylesheets/my_engine/my_engine.css \
        -c config/tailwind.config.js \
        -w
Run Code Online (Sandbox Code Playgroud)

2-对于在应用程序中的使用,显然也使用顺风,我很挣扎,因为两个生成的CSS互相咬住,我无法让两种样式在一个页面中工作。总是其中一个(应用程序或引擎)的样式不正确。直到我得到应用程序的顺风来学习引擎课程。就像这样:

添加到应用程序的 tailwind.config.js:模块之前

const execSync = require('child_process').execSync;
const output = execSync('bundle show my_engine', { encoding: 'utf-8' });
Run Code Online (Sandbox Code Playgroud)

然后在内容中作为最后一行

    output.trim() + '/app/**/*.{erb,haml,html,rb}'
Run Code Online (Sandbox Code Playgroud)

然后只需在布局中包含应用程序生成的 tailwind css,就像安装程序一样。不要在布局中包含引擎样式表,或将其添加到资源中