Rails7のnewコマンドのオプションが多すぎて分からなくなった時のために（フロントエンド多め）

Rails7から色々なオプションが増え、整理ができていなかったので、いろいろ試して整理してみました。
同じような人がいるかと思ったので、参考になると嬉しいです。

:::note
rails ver: 7.0.4.2 
:::

Rails7ではデフォルトの構成に変化が色々ありました。特にフロントエンドに関する部分が大きく変わったので、そこを重点的に説明していきます。

https://rubyonrails.org/2021/9/15/Rails-7-0-alpha-1-released

## オプションの確認

```bash
rails new -h
```

（結果は長いので省略します）

## 1. JavaScript に関するオプション

新たにJavaScriptのビルドのツールを選択できるようになりました。
デフォルトは「importmap」となっています。 (Import mapsについては後で説明します)

```bash:rails newのoption
-j, [--javascript=JAVASCRIPT]
# Choose JavaScript approach [options: importmap (default), webpack, esbuild, rollup]
# Default: importmap
```

### importmap-rails

`importmap`を選択すると、rails newで以下のコマンドが実行され、importmap-railsのデフォルト設定が作成されます。

```ruby
       rails  importmap:install
```

今まではwebpackerが使われていましたが、importmap-railsがデフォルトとなりました。
importmap-railsを使うことで、Node.jsを使ったJSファイルのビルドが不要になります。

この変更の背景について、DHHの記事で説明されています

https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755

- IE11のEOLにより、JavaScriptをES5に変換する必要がなくなった
- HTTP2の普及により、多くのファイルを1つにまとめる必要がなくなった
    - バンドルをすると少しの変更でもキャッシュのexpireが必要というデメリットがある
- [Import maps](https://github.com/WICG/import-maps)により、モジュールの依存関係にファイル名を使わずに済むので、モジュールの内容が変わっても呼び出している側のファイルのキャッシュを使い回せる

というものです。

Import mapsとは、JavaScriptモジュールをimportする際に、モジュール解決の方法を制御できるJSONオブジェクトです。モジュールと、その名前のマッピングを用意しておくことで、`import`文を使ったときに対応するモジュールを解決してくれます。[^1]

これにより、`import React from 'react'`のように書かれたJavaScriptが、そのままブラウザ上で動かせるようになります。

[^1]:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap

importmap-railsでは`config/importmap.rb`でその対応表を作ります。

```config/importmap.rb
# Pin npm packages by running ./bin/importmap

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
```

このように書くことで、モジュール解決ができ、JavaScriptをビルドしなくてもブラウザ上で動作させることができるようになります。

ちなみに、Import mapsがまだ対応していないブラウザでも、[es-module-shims](https://github.com/guybedford/es-module-shims) がインストールされるので動作するようです。

https://caniuse.com/import-maps

### その他のオプションを選択したい (bun, webpack, esbuild, rollup)

:::note
2023/9/29追記 Rails v7.1.0.beta1でBunが追加されたので別記事にまとめています
:::

https://qiita.com/kyntk/items/bd53e221f679cf7c9386

---

importmap以外にもbun, webpack, esbuild, rollupを選択することができます。
importmapだとTypeScriptを使ったり、JSXを使ったりができないので、これらを使いたいときには他のツールが候補になります。

これらを選択した場合、rails newで以下のようなコマンドが実行されます。

```ruby
       rails  javascript:install:esbuild
```

このとき、`./package.json`が生成され、esbuildがインストールされます。

そして、Railsとこのビルドツールの間をうまくつなぐ役割として [jsbundling-rails](https://github.com/rails/jsbundling-rails) があります。
`assets:precompile`を実行すると、`yarn build`の結果が`app/assets/builds`に出力され、Asset pipilineと組み合わせることができます。

https://github.com/rails/jsbundling-rails

## 2. CSS に関するオプション

```bash:rails newのoption
-c, [--css=CSS]
# Choose CSS processor [options: tailwind, bootstrap, bulma, postcss, sass... check https://github.com/rails/cssbundling-rails]
```

Tailwind CSS, Bootstrap, Bulma, PostCSS, Dart Sassが選択できます。
JavaScriptのときと同様に、以下のコマンドが実行されます。

```ruby
       rails  css:install:tailwind
```

またCSSの場合は [cssbundling-rails](https://github.com/rails/cssbundling-rails) がRailsとの間をつなぐ役割を担ってくれます。

https://github.com/rails/cssbundling-rails

CSSのオプションの中でtailwind以外は、一緒にesbuildがインストールされます。tailwindだけはデフォルトでimportmap-railsと [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) がインストールされます。

## 3. Asset pipeline に関するオプション

これも新しく追加されたオプションです。

```bash:rails newのoption
-a, [--asset-pipeline=ASSET_PIPELINE]
# Choose your asset pipeline [options: sprockets (default), propshaft]
# Default: sprockets
```

Asset pipelineについては、Sprocketsの他に新たにPropshaftが選択できるようになりました。

Asset pipelineに関するオプションを何も指定しない場合、デフォルトでSprocketsがインストールされます。不要な場合は、明示的に`--skip-asset-pipeline`オプションを指定する必要があります。

### Sprockets

元々Asset pipelineは、JavaScriptやCSSなどのアセットをバンドル、minify、圧縮、トランスパイルなどをかんたんに行うために使われていました。また、必要なプロセッサをgemを用いて組み合わせることで、柔軟に処理できました。しかし、importmap-railsがデフォルトになったことで、一部の使い方が今までと変わりました。

例えば、Sprocketsとimportmap-railsを一緒に選択した場合、トランスパイルは行われません。TypeScriptを使用するといったようにトランスパイルが必要な場合は、importmap-railsではなく、esbuildなどの別のツールを使う必要があります。

https://github.com/rails/sprockets-rails

読み込むファイルは`app/assets/config/manifest.js`に設定します。
そして`bin/rails assets:precompile`コマンドを実行してプリコンパイルすると、`public/assets`ディレクトリに配置され、配信できるようになります。
JavaScriptのファイルは`app/assets/javascripts`ではなく、`app/javascript`ディレクトリにありました。

### Propshaft

https://github.com/rails/propshaft

Propshaftは、Sprocketsに代わる新しいAsset pipeline用のライブラリです。

importmap-railsと組み合わせることで、Asset pipelineにおいてバンドルやminify、コンパイルが不要になるケースが出てきました。そこでSprocketsよりもシンプルで高速なプリコンパイルを実現する目的で開発されました。

ただし、Sprocketsを使用しているアプリケーションをPropshaftに移行する場合、多くの変更が必要になる可能性があります。例えば、Sprocketsでトランスパイルなどを行っている場合、それらの処理を別のツールに置き換える必要があります。そのため、すぐにSprocketsを代替するものにはならないようです。

しかし、新規で `rails new`をする場合、SprocketsよりもシンプルなAsset pipelineを実現できるので、候補にはなりうると思われます。

### importmap-rails や jsbundling-rails/cssbundling-rails と併用する

先程も出てきましたが、デフォルトではSprocketsと一緒にimportmap-railsもインストールされます。Asset pipelineを無効化しないかぎり、これらの組み合わせてでどれにするかを選択します。

## 4. Hotwire に関するオプション

こちらも新たに追加されたHotwireに関するオプションです。

HotwireはJavaScriptをあまり書かなくても、既存のHTMLとの組み合わせることでSPAのようなページを作ることができるツールです。

```bash:rails newのoption
[--skip-hotwire], [--no-skip-hotwire]
```

https://hotwired.dev

明示的に`--skip-hotwire`を指定しない場合、`turbo`, `stimulus`がインストールされます。

```ruby
       rails  turbo:install stimulus:install
```

gemは`turbo-rails`, `stimulus-rails`、npmパッケージは`@hotwired/turbo-rails`, `@hotwired/stimulus`です。
(importmapを使うときは`@hotwired/stimulus-loading`もインストールされました)

### Hotwire以前

Railsには、 [rails-ujs (Rails UJS)](https://github.com/rails/rails/blob/0b73361a5880d8d9d1683ceb86e2e35a421c75bc/actionview/app/javascript/README.md) と [Turbolinks](https://github.com/turbolinks/turbolinks) がありました。

Rails UJSによって、JavaScriptをほぼ書かなくてもAjaxを使ったformの挙動を実現できていました。また、Turbolinksによって、静的なHTMLであってもページ遷移をSPAっぽくすることができていました。

Rails7でHotwireが導入されるにあたり、TurbolinksはTurboの機能の1つのTurbo Driveとなっています。

また、Rails UJSはdeprecatedになりました。

https://github.com/rails/rails/pull/43112

https://railsguides.jp/working_with_javascript_in_rails.html#rails-ujs%E3%81%AE%E6%A9%9F%E8%83%BD%E3%82%92%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88%E3%82%8B

### Turbo + Stimulus

https://hotwired.dev/

HotwireはTurbo、Stimulus (とStrada)で構成されています。

#### Turbo

https://turbo.hotwired.dev

TurboはJavaScriptを書かなくてもSPAのようなページ遷移を実現できるツールです。
Turbolinksが持っていた機能(Turbo Drive)の他にもいくつか機能が追加されています。

JavaScriptを一切書かなくても、Turbo Frameの`data-turbo-frame`を使って、HTMLの一部だけを差分更新したりすることができるようになります。

#### Stimulus

https://stimulus.hotwired.dev

StimulusはJavaScriptのフレームワークです。ただ、Reactなどのように、JavaScriptがHTMLを生成するのではなく、生成されたHTMLにJavaScriptで機能を追加します。

jQuery等では、HTMLを動的に変更するには、セレクタを使って要素を選択し、イベントリスナーの設定をしますが、Stimulusではそれは行いません。HTMLに`data-controller`や`data-action`といった属性の設定をすることによって、HTMLとJavaScriptを紐付けます。

```html
<div data-controller="hello">
  <input data-hello-target="name" type="text">

  <button data-action="click->hello#greet">
    Greet
  </button>

  <span data-hello-target="output"></span>
</div>
```
```js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name", "output" ]

  greet() {
    this.outputTarget.textContent =
      `Hello, ${this.nameTarget.value}!`
  }
}
```

TurboもStimulusもRailsと切り離されたライブラリなので、素のHTMLの上でも動作させることができますし、他のフレームワークと組み合わせて使うことも可能です。

このあたりについては、他の方が色々と分かりやすい記事を書いているので、そちらを参考にしてみてください。

https://qiita.com/nazomikan/items/62cf9ff1b4edfd28c225

https://qiita.com/scivola/items/f8bda0cca91f486f56cd

https://qiita.com/kaorumori/items/6d26cc8117ccdb7ce879

https://qiita.com/kato-hiroaki/items/63acb6e988cc29c19821

https://zenn.dev/en30/articles/2e8e0c55c128e0

## 5. APIモード に関するオプション

APIモードは元々ありましたが、試してみました。

```bash:rails newのoption
[--api], [--no-api] # Preconfigure smaller stack for API only apps
```

APIモードにすると、config/application.rbで`config.api_only = true`が設定されますが、それ以外にも不要なファイルや設定が読み込まれなくなります。

<details><summary>ファイルやディレクトリを削除しているログ</summary>

```bash
      remove  app/assets
      remove  lib/assets
      remove  tmp/cache/assets
      remove  app/helpers
      remove  test/helpers
      remove  app/views/layouts/application.html.erb
      remove  public/404.html
      remove  public/422.html
      remove  public/500.html
      remove  public/apple-touch-icon-precomposed.png
      remove  public/apple-touch-icon.png
      remove  public/favicon.ico
      remove  config/initializers/assets.rb
      remove  app/assets/config/manifest.js
      remove  app/assets/config
      remove  app/assets/stylesheets/application.css
      remove  config/initializers/content_security_policy.rb
      remove  config/initializers/permissions_policy.rb
      remove  config/initializers/new_framework_defaults_7_0.rb
```

</details>

ソース: [railties/lib/rails/generators/rails/app/app_generator.rb](https://github.com/rails/rails/blob/e90ca08d8061614c58d2b91762cbb10e1aa6058a/railties/lib/rails/generators/rails/app/app_generator.rb)

またimply_options[^2]として、 `--skip-asset-pipeline`, `--skip-javascript`, `--skip-hotwire`も設定されます。

[^2]: https://github.com/rails/rails/blob/4716bed6faba761ee945ea35a9a8e34e9035b671/railties/lib/rails/generators/app_base.rb#L185-L209

## 6. minimal (最小限のRailsアプリケーション)

minimalも元々ありましたが、使ったことがなかったので試してみました。

```bash:rails newのoption
[--minimal], [--no-minimal] # Preconfigure a minimal rails app
```

minimalはメタオプションです。メタオプションとは、その他の複数のオプションをまとめて指定するためのオプションです。

ちなみに、`api`はオプションをまとめる以外の機能もあるので、メタオプションではありません。

minimalをつけると、以下のオプションをまとめて指定することになります。

- skip_action_cable
- skip_action_mailbox
- skip_action_mailer
- skip_action_text
- skip_active_job
- skip_active_storage
- skip_bootsnap
- skip_dev_gems
- skip_hotwire
- skip_javascript
- skip_jbuilder
- skip_system_test

そのため、最小構成のRailsアプリケーションを作りたいときに`rails new hoge --skip-action-mailer --skip-action-mailbox --skip-action-cable ...`のようにたくさんオプションをつけなくてもよくなるので便利です。

https://github.com/rails/rails/blob/e90ca08d8061614c58d2b91762cbb10e1aa6058a/railties/lib/rails/generators/rails/app/app_generator.rb

https://github.com/rails/rails/blob/4716bed6faba761ee945ea35a9a8e34e9035b671/railties/lib/rails/generators/app_base.rb

minimailを設定してもtest_unitはインストールされてしまうので、RSpecを使いたいときは`-T`をつけると良いです。

### その他のimply options

`api`, `minimal`以外にも、これを指定すると、他のオプションも指定されるというものがあります。

- skip_active_job: `skip_action_mailer`, `skip_active_storage`
- skip_active_record: `skip_active_storage`
- skip_active_storage: `skip_action_mailbox`, `skip_action_text`
- skip_javascript: `skip_hotwire`

詳しくはソースコードを読んでみてください。

- [rails/railties/lib/rails/generators/app_base.rb · GitHub](https://github.com/rails/rails/blob/4716bed6faba761ee945ea35a9a8e34e9035b671/railties/lib/rails/generators/app_base.rb)
- [rails/railties/lib/rails/generators/rails/app/app_generator.rb · GitHub](https://github.com/rails/rails/blob/e90ca08d8061614c58d2b91762cbb10e1aa6058a/railties/lib/rails/generators/rails/app/app_generator.rb)

## 感想

最近は、Asset pipelineを使っていないということもあり、Import mapsとかAsset pipeline周りとか、Hotwireとか知っても使わないしなーと思っていたのですが、調べてみるとどうしてこの変更をしたのか等が知れて、とてもおもしろかったです。

正直、bundleやminifyが必要ない!というのは早い気がしますが、今のフロントエンドのトレンドに対して、違った観点からの考え方が知れて新鮮でした。

## 参考

- Rails 7
    - [Ruby on Rails — Rails 7.0 Alpha 1: New JavaScript Answers, At-Work Encryption, Query Origin Logging, Zeitwerk Exclusively](https://rubyonrails.org/2021/9/15/Rails-7-0-alpha-1-released)
    - [Rails 7 : rails newのフロントエンド関連オプションの組み合わせを調べてみた｜TechRacho by BPS株式会社](https://techracho.bpsinc.jp/hachi8833/2021_12_28/114730)
    - [rails new で指定するフラグによって生成されるコードの違いを確認するリポジトリを作った - Qiita](https://qiita.com/snaka/items/d60fbf6e26ce42237de8)
    - [妄想的DHH理解 - Qiita](https://qiita.com/nazomikan/items/eab3352dbd5e2b79724b)
    - [Rails7がもつフロントエンドへの「答え」](https://zenn.dev/kenzan100/articles/0f9b100655a4bf)
- importmap
    - [WICG/import-maps: How to control the behavior of JavaScript imports](https://github.com/WICG/import-maps)
    - [Webpacker から importmap-rails に移行した - Qiita](https://qiita.com/mishina2228/items/d4b9af22d0096ee451d7)
- Asset pipeline
    - [Update the Asset Pipeline guide by matuszewskijan · Pull Request #47012 · rails/rails](https://github.com/rails/rails/pull/47012)
    - [アセットパイプライン - Railsガイド](https://railsguides.jp/asset_pipeline.html)
    - [Modern web apps without JavaScript bundling or transpiling](https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755)
- Hotwire
    - [HTML Over The Wire | Hotwire](https://hotwired.dev/)
    - [Hotwireで検索機能を作ってみる - Qiita](https://qiita.com/kato-hiroaki/items/63acb6e988cc29c19821)
    - [Hotwireとは何なのか？](https://zenn.dev/en30/articles/2e8e0c55c128e0)
    - [Hotwireのススメ ~React製SPAをフルSSRでStimulusとTurboに書き換えた話~](https://zenn.dev/mast1ff/articles/3d6f4b9e4a38bb)
- Turbo
    - [【Hotwire】HTMLだけで分かる Turbo 🚜 - Qiita](https://qiita.com/ShinKano/items/018e32d25f40bce201de)
    - [Rails 7のHotwireがどんなことをしてくれるものかを知りたいです - Qiita](https://qiita.com/kagesumi3m/items/c19e1cc3430b88d8bbec)
    - [Hotwire勉強会ログ (1) Turbo - Qiita](https://qiita.com/kaorumori/items/6d26cc8117ccdb7ce879)
    - [Rails で JavaScript を利用する - Railsガイド](https://railsguides.jp/working_with_javascript_in_rails.html)
- Stimulus
    - [Stimulus: A modest JavaScript framework for the HTML you already have.](https://stimulus.hotwired.dev/)
    - [第三の答え、Stimulusjs - Qiita](https://qiita.com/nazomikan/items/62cf9ff1b4edfd28c225)
    - [Stimulus に入門した（最小構成） - Qiita](https://qiita.com/scivola/items/f8bda0cca91f486f56cd)
- rails-ujs, turbolinks
    - [rails/working_with_javascript_in_rails.md at f63980173ed30a98a91628c14015fd9ce2c76600 · rails/rails · GitHub](https://github.com/rails/rails/blob/f63980173ed30a98a91628c14015fd9ce2c76600/guides/source/working_with_javascript_in_rails.md)
- minimal
    - [rails/railties/lib/rails/generators/app_base.rb · GitHub](https://github.com/rails/rails/blob/4716bed6faba761ee945ea35a9a8e34e9035b671/railties/lib/rails/generators/app_base.rb)
    - [rails/railties/lib/rails/generators/rails/app/app_generator.rb · GitHub](https://github.com/rails/rails/blob/e90ca08d8061614c58d2b91762cbb10e1aa6058a/railties/lib/rails/generators/rails/app/app_generator.rb)