如何在Rails 7引擎中设置importmap-rails?

9
我已经在importmap-rails gem的Github仓库这里开了一个问题,但我想在这里提出问题,以防有人能提供解决方案。
到目前为止,我发现以下情况:
使用rails plugin new custom_page --mountable --full在Rails 7 alpha2或Rails 7.0中生成的新引擎将importmap-rails gem包含在捆绑的gems中,但无法使用。在enginename.gemspec中添加spec.add_dependency 'importmap-rails'没有任何效果,也不会在engine.rb中添加require importmap-rails。bin目录中没有importmap可执行文件。
调用bundle info importmap-rails会产生一个有希望的结果,显示默认安装了该gem。
* importmap-rails (0.8.1)
    Summary: Use ESM with importmap to manage modern JavaScript in Rails without transpiling or bundling.
    Homepage: https://github.com/rails/importmap-rails
    Source Code: https://github.com/rails/importmap-rails
    Path: /home/jamie/.rvm/gems/ruby-3.0.0@custom_page/gems/importmap-rails-0.8.1

运行rails --tasks命令会显示:

rails app:importmap:install # Setup Importmap for the app

我认为这是由--full选项生成的测试应用程序引起的,而不是作为引擎的rails命令可用。 我期望看到相同的内容,而没有app:前缀。 对该任务的调用会导致模板错误,如下所示。

rails app:importmap:install

不知道如何构建任务'app:template'(查看可用任务列表:rails --tasks)。您是不是想说'app:tmp:create'

如果有解决此问题的变通方法,我会非常感激听到它,我相信其他人也是如此。我之所以想要解决这个问题,是因为我在Rails 6.1.4引擎中完全无法介绍Webpacker,而我希望这将成为我的改进解决方案。


1
实际上,您没有 app:template,但应该有 app:app:template。我的解决方法是创建一个别名 rake 任务来绕过此错误。在您的 rake 文件中:desc 'Alias to app:app:template' task template: :environment do Rake::Task['app:app:template'].invoke end - gillien
2个回答

14

您不需要使用安装任务来设置导入地图。它所做的只是一些复制粘贴操作,并且实际上并没有帮助引擎的设置。

将导入地图添加到引擎的gemspec文件中:

# my_engine/my_engine.gemspec

spec.add_dependency "importmap-rails"

更新 engine.rb 文件:

# my_engine/lib/my_engine/engine.rb

require "importmap-rails"

module MyEngine
  class Engine < ::Rails::Engine
    isolate_namespace MyEngine
    
    initializer "my-engine.importmap", before: "importmap" do |app|
      # NOTE: this will add pins from this engine to the main app
      # https://github.com/rails/importmap-rails#composing-import-maps
      app.config.importmap.paths << root.join("config/importmap.rb")

      # NOTE: something about cache; I did not look into it.
      # https://github.com/rails/importmap-rails#sweeping-the-cache-in-development-and-test
      app.config.importmap.cache_sweepers << root.join("app/assets/javascripts")
    end

    # NOTE: add engine manifest to precompile assets in production
    initializer "my-engine.assets" do |app|
      app.config.assets.precompile += %w[my_engine_manifest]
    end
  end
end

更新资产清单:
# my_engine/app/assets/config/my_engine_manifest.js

//= link_tree ../javascripts/my_engine .js

为我们的引擎添加JavaScript入口点(如果需要)。即使没有此文件,也可以使用Pins。
# my_engine/app/assets/javascripts/my_engine/application.js

// do some javascript
document.querySelector("h1").innerText = "hi, i'm your engine";
console.log("hi, again");

更新引擎的布局:

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

<!DOCTYPE html>
<html>
  <head>
    <!--
      NOTE: This loads/imports main app `application.js` and all the pins from
            the main app and from the engine (because we set it up in the engine.rb).
    -->
    <%= javascript_importmap_tags %>

    <!--
      NOTE: To add engine's javascript functionality we have to load the
            entrypoint here or `import` it in the main app `application.js`
    -->
    <%= javascript_import_module_tag "my_engine/application" %>
  </head>
  <body> <%= yield %> </body>
</html>

创建 importmap.rb 并固定 my_engine/application,这个名称必须与 javascript_import_module_tag 匹配。它不能与主应用程序中的任何其他名称冲突,因此您不能仅使用 application
# my_engine/config/importmap.rb

# NOTE: this pin works because `my_engine/app/assets/javascripts
#       is in the `Rails.application.config.assets.paths`
pin "my_engine/application"

一些额外的测试来验证设置是否正确:

# config/routes.rb
Rails.application.routes.draw do
  mount MyEngine::Engine => "/"
end

# my_engine/config/routes.rb
MyEngine::Engine.routes.draw do
  get "home", to: "homes#index"
end

# my_engine/app/controllers/my_engine/homes_controller.rb
module MyEngine
  class HomesController < ApplicationController
    def index; end
  end
end

# my_engine/app/views/my_engine/homes/index.html.erb
<h1>Home</h1>

此时,您的渲染布局的<head>标记中应该有以下内容,以及其他内容:

<script type="importmap" data-turbo-track="reload">{
  "imports": {
    "application": "/assets/application-66ce7505c61e3e4910ff16e7c220e1fbfb39251cd82e4bab8d325b3aae987cf9.js",
    "my_engine/application": "/assets/my_engine/application-31ce493e8376b4c20703a50f38d419ae309ffe410b7ab7fec47440e02eef08a8.js",
  }
}</script>

<script type="module">import "application"</script>
<script type="module">import "my_engine/application"</script>

H1标签应在重新加载时更改为<h1>hi, i'm your engine</h1>

可以使用https://generator.jspm.io/手动添加其他的importmaps。

如果想要额外加分,可以自定义bin/importmap以使其在引擎内工作。在bin目录下创建一个新的importmap文件。

# my_engine/bin/importmap

#!/usr/bin/env ruby

# NOTE: don't forget to `chmod u+x bin/importmap` to make it executable.

# make sure we are loading the correct versions of things
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])

# NOTE: importmap requires some rails goodness that we don't have in the engine,
#       because we don't have `config/application.rb` that loads the environment.
require "rails"

# importmap-rails is not loaded automatically
require "importmap-rails"

# the actual command runner
require "importmap/commands"

从引擎目录内运行:

$ bin/importmap pin react  
Pinning "react" to https://ga.jspm.io/npm:react@18.1.0/index.js

$ cat config/importmap.rb 
pin "my_engine/application"
pin "react", to: "https://ga.jspm.io/npm:react@18.1.0/index.js"

我没有经过太多测试,所以欢迎任何反馈。如果有什么不显示的地方,请重启服务器,我不知道重新加载所有这些文件的行为方式。


1
非常感谢您提供的又一份详细答案。我会尽快检查并测试,如果对我有效,我会告诉您的。 - jamesc
1
可以使用单个 <%= javascript_importmap_tags "my_engine/application" %> 代替 <%= javascript_importmap_tags %><%= javascript_import_module_tag "my_engine/application" %> 的两个标签。 - Alex L.
现在重新审视一下,有两个问题。首先,在bin/importmap文件中,#!/usr/bin/env ruby声明必须出现在文件的第一行,而不是文件名的注释。通过这个改变,bin/importmap pin react可以正常工作。第二个问题是,<%= javascript_importmap_tags %>生成了一个空的导入集,即"imports": { }。不知道为什么会这样,我现在正在设置这个,以便能够在我正在开发的一个字段集中使用stimulus来添加和删除字段。我还没有检查stimulus是否能够正常工作,但我假设,除非导入集被填充,否则它不会起作用。 - undefined
谢谢Alex,我已经更新了我的问题。 - undefined
1
太棒了,在引擎的初始化程序中添加app.config.importmap.paths << root.join("config/importmap.rb")解决了我的问题,再次感谢你,如果你有机会来利兹,我一定要请你喝一杯。不需要在虚拟测试应用程序中添加importmap。 - undefined
显示剩余2条评论

3
我退回到在html中使用老式的Javascript include。
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>

它绝对可以在所有浏览器上运行,而不是找出浏览器是否支持我以后可能使用的功能。

我有完全的控制权来决定将页面放在哪里...但这可能不是你想要的...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接