🍉

Railsのstylehseet_pack_tagについて調べてみた

8 min read

デフォルト設定の場合

Ruby on Railsのv6を指定してrails new _6.0.0_ test -d mysqlした直後のプロジェクトのapp/views/layouts/application.html.erbはデフォルトで以下のようになっています。

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

このデフォルトの状態で正常にCSSなどのファイルが読み込まれるのか確認するため以下の設定をしました。

  • app/javascript/packsにtest.jsを作成し、console.log('test')を記述
  • app/javascript/packs/appliction.jsに、imoprt 'packs/test'を追記
  • app/assets/stylesheetsにtest.cssを作成し body{color: red;}を記述

app/assets/stylesheets/application.css には *= require_tree .が記述されており、app/assets/stylesheetsのディレクトリ配下のCSSファイルは自動的に読み込まれますが、app/javascript/packs/application.js にはそのような記述がないため、都度ファイルを読み込むための記述をします。(もしディレクトリは以下を読み込む記述があれば教えていただきたいです。)

上記のように編集して、developmentモード、productionモードの両方railsサーバーを起動してもエラーは起きず、追記したCSS・JSが反映された画面がエラーなく正常に表示されます。

ちなみにproductionモードでrailsサーバーを起動するには以下のコマンドを実行します。

bundle exec rails assets:clobber RAILS_ENV=production
bundle exec rails assets:precompile RAILS_ENV=production
rails s -e production

※また、NginxなどのWebサーバーなどを使っていない場合には、config/environments/production.rbの設定を以下のように変更します。

config.public_file_server.enabled = true

設定の詳細の中身は以下の記事に書かれていますのでご参考までに。

https://qiita.com/at-946/items/b7d467bf25c40fcfca44

stylesheet_pack_tagがproduction環境でエラーが出る

デフォルト設定に以下のように stylesheet_pack_tag を追記して、railsサーバーをdevelopmentモードで動かすと特にエラーは起きませんでした。

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

ただし、これをproductionモードで動かすとエラーが起きてしまいます。つまり、開発時には特にエラーは起きていないのに、本番リリース時にはエラーが起きてしまうということになります。

ログを見ると以下のようなエラーが吐かれています。

F, [2021-07-09T19:31:22.706284 #66663] FATAL -- : [e424266f-a5da-4358-b924-5cc0cbcb9258]   
[e424266f-a5da-4358-b924-5cc0cbcb9258] ActionView::Template::Error (Webpacker can't find application.css in /Users/saitouryouzi/developer/jeans/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-c3e3aae48c4c321181e9.js",
  "application.js.map": "/packs/js/application-c3e3aae48c4c321181e9.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-c3e3aae48c4c321181e9.js"
      ],
      "js.map": [
        "/packs/js/application-c3e3aae48c4c321181e9.js.map"
      ]
    }
  }
}
):
[e424266f-a5da-4358-b924-5cc0cbcb9258]      7:     <%= csp_meta_tag %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]      8: 
[e424266f-a5da-4358-b924-5cc0cbcb9258]      9:     <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     10:     <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     11:     <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     12:   </head>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     13: 
[e424266f-a5da-4358-b924-5cc0cbcb9258]   
[e424266f-a5da-4358-b924-5cc0cbcb9258] app/views/layouts/application.html.erb:10

開発(developmentモード)時にはエラーが起きなくて、本番(productionモード)時にはエラーが起きる理由は以下の記事に詳細が書いてありました。

https://qiita.com/kazutosato/items/23d2aa126084d054398b

javascript_pack_tag と stylesheet_pack_tag の仕様はこうなってます:development環境では、JavaScriptを使って動的にCSSを埋め込む。CSSファイルを生成してlinkタグに指定することはしない。

つまり、javascript_pack_tagstylesheet_pack_tagはdevelopmentモードではlinkなどは生成していない。しかし、productionモードではlinkを生成する形になり、その参照先が何もないためエラーが発生するようです。
もっと正確に記述すると、config/webpacker.ymlの中でextract_css: trueの場合にはlinkを出力し、extract_css: falseの場合にはJavaScriptを使って動的にCSSを読み込むという動作になります。

GitHubでstylesheet_pack_tagの実装を見てみると、そのようなコメントがありました。

https://github.com/rails/webpacker/blob/d456b1b3783313bf0b3927bed6f434d1de5854c4/lib/webpacker/helper.rb#L131-L147

When extract_css is false in webpacker.yml:
<%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # => nil
When extract_css is true in webpacker.yml:
<%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # => <link rel="stylesheet" media="screen" href="/packs/calendar-1016838bab065ae1e122.css" data-turbolinks-track="reload" />

実際に、config/webpacker.ymlの中身を見てみると以下のようになっています。(長いので関係ない箇所は省略しました。)

# Note: You must restart bin/webpack-dev-server for changes to take effect

default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_root_path: public
  public_output_path: packs
  cache_path: tmp/cache/webpacker
  webpack_compile_output: true

  # Additional paths webpack should lookup modules
  # ['app/assets', 'engine/foo/app/assets']
  additional_paths: []

  # Reload manifest.json on all requests so we reload latest compiled packs
  cache_manifest: false

  # Extract and emit a css file
  extract_css: false

  static_assets_extensions:
    ...

  extensions:
    ...

development:
  <<: *default
  compile: true

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost
    port: 3035
    public: localhost:3035
    hmr: false
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    pretty: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: '**/node_modules/**'

test:
  <<: *default
  compile: true

  # Compile test packs to a separate directory
  public_output_path: packs-test

production:
  <<: *default

  # Production depends on precompilation of packs prior to booting for performance.
  compile: false

  # Extract and emit a css file
  extract_css: true

  # Cache manifest.json for performance
  cache_manifest: true

development環境では、extract_css: falseとなっており、production環境では、extract_css: trueとなっています。
そのため、development環境ではJavaScriptで動的にCSSを読み込む形になっていました。

試しに、config/webpacker.ymlのdeveloment環境のextract_css: trueに設定して、ailsサーバーをdevelopmentモードで動かすと、さきほどproduction環境で出たエラーと同じエラーが発生しました。

stylesheet_pack_tagの使い所

stylesheet_pack_tagの使いどころは、JavaScriptだけでなくCSSもWebpackerで管理したい時になります。(個人的な考えとして、Railsの標準ではそのような設定ではないので、極力CSSはWebpackerで扱わない方がいいのではないかと思っています。)

https://techracho.bpsinc.jp/hachi8833/2021_05_06/83678

WebpackerでCSSを管理するのに必要な設定は以下の通りです。

  • stylesheet_link_tagstylesheet_pack_tagに変更する
  • app/javascript/application.jsに、利用したいCSSファイルをインポートする

今回はテストのために、app/javascript/stylesheets/test.cssを作成して、app/javascript/application.jsimport 'stylesheets/test'を記述してCSSが読み込まれるかテストしました。

<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
body{
  color: blue;
}
import 'stylesheets/test'

このようにして、developmentモード(config/webpacker.ymlのdevelopment環境はextract_css: falseに設定)でrailsサーバーを起動すると、エラーなどは起きずにCSSが正常に読み込まれていました。
これと全く同じ形でproductionモード(config/webpacker.ymlのdevelopment環境はextract_css: trueに設定)でrailsサーバーを起動してみても、エラーなどは起きずにCSSが正常に読み込まれていました。