简体   繁体   English

如何在 Rails 7 引擎中使用顺风 css gem?

[英]How to use tailwind css gem in a rails 7 engine?

How to use tailwind in a rails engine?如何在 Rails 引擎中使用 Tailwind? According to the documentation supplying a css argument to the Rails generator should work根据向 Rails 生成器提供 css 参数的文档应该可以工作

Rails 7.0.2.2 engine generated using使用生成的 Rails 7.0.2.2 引擎

rails plugin new tailtest --mountable --full -d postgresql --css tailwind

This generates the engine with Postgresql but does nothing with tailwind at all, and following manual installation instructions fail too.这会生成带有 Postgresql 的引擎,但对 tailwind 根本没有任何作用,并且遵循手动安装说明也会失败。

Running, as per documentation , bundle add tailwindcss-rails adds tailwind to the gemfile rather than the engines tailtest.gemspec So after adding the dependency to the gemspec根据文档运行, bundle add tailwindcss-rails将 tailwind 添加到 gemfile 而不是引擎 tailtest.gemspec 所以在将依赖项添加到 gemspec 之后

spec.add_dependency "tailwindcss-rails", "~> 2.0"

and running bundle install does install the engine however the rest of the manual installation fails并且运行bundle install确实安装了引擎但是手动安装的 rest 失败

then adding the require to lib/engine.rb然后将要求添加到 lib/engine.rb

require "tailwindcss-rails"
module Tailtest
  class Engine < ::Rails::Engine
    isolate_namespace Tailtest
  end
end

then running the install process fails然后运行安装过程失败

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

Obviously the app:tailwindcss:install command fails too.显然app:tailwindcss:install命令也失败了。

So I am probably missing an initializer of some sort in the engine.rb file but no idea on what it should be.所以我可能在 engine.rb 文件中遗漏了某种初始化程序,但不知道它应该是什么。

It is the same idea as How to set up importmap-rails in Rails 7 engine?How to set up importmap-rails in Rails 7 engine?思路相同。 . . We don't need to use the install task.我们不需要使用安装任务。 Even if you're able to run it, it's not helpful in the engine (see the end of the answer for explanation).即使您能够运行它,它对引擎也没有帮助(请参阅答案的末尾以获取解释)。

Also rails plugin new doesn't have a --css option. rails plugin new也没有--css选项。 To see available options: rails plugin new -h .查看可用选项: rails plugin new -h

Update engine's gemspec file:更新引擎的 gemspec 文件:

# my_engine/my_engine.gemspec

spec.add_dependency "tailwindcss-rails"

Update engine.rb :更新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

Update assets manifest:更新资产清单:

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

//= link_tree ../builds/ .css

Update engine's layout:更新引擎的布局:

# 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>

bundle show command will give us the path where the gem is installed, so we can copy a few files: bundle show命令将为我们提供安装 gem 的路径,因此我们可以复制一些文件:

$ 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

Copy tailwind.config.js file from tailwindcss-rails :tailwindcss-rails复制tailwind.config.js文件:

$ cp $(bundle show tailwindcss-rails)/lib/install/tailwind.config.js config/tailwind.config.js

Copy application.tailwind.css file into any directory to fit your setup:application.tailwind.css文件复制到任何目录以适合您的设置:

$ cp $(bundle show tailwindcss-rails)/lib/install/application.tailwind.css app/assets/stylesheets/application.tailwind.css

Because tailwindcss-rails uses standalone executable, we don't need node or rails to compile the stylesheets.因为tailwindcss-rails使用独立的可执行文件,所以我们不需要节点或 rails 来编译样式表。 We just need to get to the executable itself.我们只需要访问可执行文件本身。

Executable is located herehttps://github.com/rails/tailwindcss-rails/tree/v2.0.8/exe/ .可执行文件位于此处https://github.com/rails/tailwindcss-rails/tree/v2.0.8/exe/ Instead of running the build task https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake we can just call the executable directly.我们可以直接调用可执行文件,而不是运行构建任务https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake

$ $(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

Use -w option to start watch mode.使用-w选项启动监视模式。

$ $(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

The output file should match the name in stylesheet_link_tag "my_engine" . output 文件应与stylesheet_link_tag "my_engine"中的名称匹配。

Now that you have a plain my_engine.css file, do with it what you want.现在您有了一个普通的my_engine.css文件,可以根据需要对其进行操作。 Use it in the layout, require it from the main app application.css .在布局中使用它,从主应用程序 application.css需要它。 The usual rails asset pipeline rules apply.通常的 Rails 资产管道规则适用。

If you want to put all that into a task, use Engine.root to get the paths.如果您想将所有这些放入任务中,请使用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

From the engine directory:从引擎目录:

$ 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.

Make your own install task if you have a lot of engines to set up:如果您要设置很多引擎,请创建自己的安装任务:

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

Install task reference:安装任务参考:
https://github.com/rails/rails/blob/v7.0.2.4/railties/lib/rails/tasks/framework.rake#L8 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/install.rake

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


Update How to merge two tailwinds.更新如何合并两个顺风。

Above setup assumes the engine is its own separate thing, like admin backend, it has its own routes, templates, and styles. If an engine functionality is meant to be mixed with the main app, like a view_component collection, then tailwind styles will override each other.上面的设置假设引擎是它自己独立的东西,比如管理后端,它有自己的路由、模板和 styles。如果引擎功能打算与主应用程序混合,比如view_component集合,那么 tailwind styles 将覆盖彼此。 In this case isolating engine styles with a prefix could work:在这种情况下,使用前缀隔离引擎 styles 可能会起作用:
https://tailwindcss.com/docs/configuration#prefix https://tailwindcss.com/docs/configuration#prefix

The reason that tailwind styles don't mix is because most of the selectors have the same specificity and the order is very important. tailwind styles 不混合的原因是因为大多数选择器具有相同的特异性并且顺序非常重要。

So here is an example.所以这是一个例子。 Main app with an engine, both using tailwind, both compile styles separately, tailwind configs are only watching one file from the engine and one from the main app, only using @tailwind utilities;带有引擎的主应用程序,都使用 tailwind,都分别编译 styles,tailwind 配置只监视引擎中的一个文件和主应用程序中的一个文件,仅使用@tailwind 实用程序; directive:指示:

Engine template, that we want to use in the main app, should work fine:我们想在主应用程序中使用的引擎模板应该可以正常工作:

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

<div class="bg-red-500 sm:bg-blue-500"> red never-blue </div>

But when rendered in the main app it never turns blue.但是当在主应用程序中呈现时,它永远不会变成蓝色。 Here is the demonstration set up:这是演示设置:

<!-- app/views/home/index.html.erb -->

<%= stylesheet_link_tag "blep",     "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>

<!-- output generated css in the same order as above link tags -->
<% require "open-uri" %>
<b>Engine css</b>
<pre><%= URI.open(asset_url("blep")).read %></pre>
<b>Main app css</b>
<pre><%= URI.open(asset_url("tailwind")).read %></pre>

<div class="bg-red-500"> red </div> <!-- this generates another bg-red-500 -->
<br>
<%= render "blep/partial" %>

And it looks like this:它看起来像这样:

 /* Engine css */.bg-red-500 { --tw-bg-opacity: 1; background-color: rgb(239 68 68 / var(--tw-bg-opacity)) } @media (min-width: 640px) {.sm\:bg-blue-500 { --tw-bg-opacity: 1; background-color: rgb(59 130 246 / var(--tw-bg-opacity)) } } /* Main app css */.bg-red-500 { --tw-bg-opacity: 1; background-color: rgb(239 68 68 / var(--tw-bg-opacity)) }
 <div class="bg-red-500"> red </div> <br> <div class="bg-red-500 sm:bg-blue-500"> red never-blue </div>

^ you can hit run and click "full page". ^ 你可以点击运行并点击“整页”。 Main app bg-red-500 selector is last so it overrides engines sm:bg-blue-500 selector, media queries don't add to specificity score.主应用程序bg-red-500选择器在最后,因此它会覆盖引擎sm:bg-blue-500选择器,媒体查询不会增加特异性分数。 It's the same reason you can't override, say, mt-1 with m-2 , margin top comes later in the stylesheet.这与您不能用m-2覆盖mt-1的原因相同,margin top 出现在样式表的后面。 This is why @layer directives are important.这就是@layer指令很重要的原因。

The only way around this is to watch the engine directory when running tailwind in the main app, so that styles are compiled together and in the correct order.解决这个问题的唯一方法是在主应用程序中运行 tailwind 时查看引擎目录,以便 styles 以正确的顺序一起编译。 Which means you don't really need tailwind in the engine:这意味着您真的不需要引擎中的顺风:

module.exports = {
  content: [
    "./app/**/*",
    "/just/type/the/path/to/engine/views",
    "/or/see/updated/task/below",
  ],
}

Other ways I tried, like running 6 tailwind commands for each layer for main app and engine, so that I can put them in order, better but was still out of order a bit and duplicated.我尝试过的其他方法,比如为主应用程序和引擎的每一层运行 6 个顺风命令,这样我就可以将它们按顺序排列,效果更好,但仍然有点乱序和重复。 Or doing an @import and somehow letting postcss-import know where to look for engine styles (I don't know, I just symlinked it into node_modules to test), but this still required tailwind to watch engine files.或者做一个@import并以某种方式让postcss-import知道在哪里寻找引擎 styles(我不知道,我只是将它符号链接到node_modules进行测试),但这仍然需要顺风来观察引擎文件。


I did some more digging, tailwind cli has a --content option, which will override content from tailwind.config.js .我做了更多的挖掘, tailwind cli 有一个--content选项,它将覆盖来自tailwind.config.jscontent We can use it to setup a new task:我们可以用它来设置一个新任务:

namespace :tailwindcss do
  desc "Build your Tailwind CSS + Engine"
  task :watch do |_, args|
    # NOTE: there have been some updates, there is a whole Commands class now
    #       lets copy paste and modify.          (debug = no --minify)
    command = Tailwindcss::Commands.watch_command(debug: true, poll: false)

    # --content /path/to/app/**/*,/path/to/engine/**/*
    command << "--content"
    command << [
      Rails.root.join("app/views/home/*"),
      Blep::Engine.root.join("app/views/**/*.erb")
    ].join(",")

    p command
    system(*command)
  end

  # same for build, just call `compile_command`
  # task :build do |_, args|
  #   command = Tailwindcss::Commands.compile_command(debug: false)
  #   ...
end

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

That answer by Alex is really good, i wish i had it when starting out.亚历克斯的回答非常好,我希望我在开始的时候就知道了。 (But i didn't even have the question to google) Just want to add two things: (但我什至没有谷歌的问题)只想添加两件事:

1- a small simplification. 1- 一个小的简化。 I just made a script to run tailwind in the engine我刚刚制作了一个脚本来在引擎中运行 tailwind

#!/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

2- For usage in an app, that obviously also uses tailwind, i was struggling, since the two generated css's were biting each other and i could not get both styles to work in one page. 2-对于在应用程序中的使用,显然也使用顺风,我很挣扎,因为两个生成的 css 互相咬住,我无法让 styles 在一页中工作。 Always one or the other (app or engine) was not styled right.总是一个或另一个(应用程序或引擎)的样式不正确。 Until i got the app's tailwind to pick up the engines classes.直到我得到应用程序的顺风来获取引擎类。 Like so:像这样:

Add to the app's tailwind.config.js: before the module添加到应用程序的 tailwind.config.js:模块之前

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

And then inside the content as last line然后在内容里面作为最后一行

    output.trim() + '/app/**/*.{erb,haml,html,rb}'

Then just include the apps generated tailwind css in the layout, like the installer will.然后只需在布局中包含生成的 tailwind css 应用程序,就像安装程序一样。 Don't include the engines stylesheet in the layout, or add it to the asset不要在布局中包含引擎样式表,或将其添加到资产中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM