rails7+esbuild+bootstrap5で環境構築してcssとjavascriptが当たっているか確認する最短の方法

### 記事を書いた経緯

自分自身がお仕事でbootstrap5の導入とesbuildを使ったwebアプリ開発に携わった中で最短で環境構する方法が知りたかったから。

### 本記事のゴール

bootstrap+esbuildで環境構築して、簡単なrailsのtodoアプリを作成する。アプリ自体はgenerateorでscaffoldを作成した際にできたデフォルトのもの。

### esbuildってそもそも何？

esbuild(イーエスビルド)は、javascriptのいわゆるバンドラーと呼ばれる役割のもので、同じような役割をするものにimportmapやrollupがあります。筆者はimportmapとesbuildのみ使ったことがあります。ちなみに、javascriptのバンドラーはrails newの時に指定することができるので現在すでに動いているアプリの環境を移行したいとかでなければnew時に指定することをおすすめします。ちなみに、バンドラーはcss用とjavascript用の２種類あるので両方サポートしているimportmapやwebpackなどを使わない場合にはcssbundling-railsというgemでcssのバンドラーも指定してあげる必要があります。

### 動作環境の前提

環境構築時には、homebrewをはじめとしたmacOSでのrailsの環境構築がされている前提とします。最低限、anyenv>rbenv>npm>yarn はインストールしておいてください（>の順で環境構築するとうまく行くと思います→念の為パッケージ管理ツールのドキュメントは読んでおくことをおすすめします）。

### 実際にやってみる

- rails newして新規プロジェクトを立ち上げる（cssをbootstrapで指定する）

```bash
rails new Myapp --css bootstrap
```

```bash
create  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
      create  Gemfile
         run  git init from "."
Initialized empty Git repository in /Users/kumagaikouhei/Documents/rails_practice/Myapp/.git/
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/assets/images
      create  app/assets/images/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/cable.yml
      create  config/puma.rb
      create  config/storage.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/content_security_policy.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/new_framework_defaults_7_0.rb
      create  config/initializers/permissions_policy.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/master.key
      append  .gitignore
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/apple-touch-icon-precomposed.png
      create  public/apple-touch-icon.png
      create  public/favicon.ico
      create  public/robots.txt
      create  tmp
      create  tmp/.keep
      create  tmp/pids
      create  tmp/pids/.keep
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor
      create  vendor/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/channels/application_cable/connection_test.rb
      create  test/test_helper.rb
      create  test/system
      create  test/system/.keep
      create  test/application_system_test_case.rb
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_7_0.rb
         run  bundle install
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Using rake 13.0.6
Fetching concurrent-ruby 1.2.2
Fetching minitest 5.18.0
Using builder 3.2.4
Using erubi 1.12.0
Using websocket-extensions 0.1.5
Fetching rack 2.2.7
Using marcel 1.0.2
Using crass 1.0.6
Fetching nio4r 2.5.9
Fetching timeout 0.3.2
Using mini_mime 1.1.2
Using date 3.3.3
Using racc 1.6.2
Using public_suffix 5.0.1
Using bindex 0.8.1
Using bundler 2.3.14
Fetching regexp_parser 2.8.0
Fetching msgpack 1.7.0
Using matrix 0.4.2
Using method_source 1.0.0
Using thor 1.2.1
Fetching zeitwerk 2.6.7
Installing minitest 5.18.0
Installing concurrent-ruby 1.2.2
Installing nio4r 2.5.9 with native extensions
Installing zeitwerk 2.6.7
Installing msgpack 1.7.0 with native extensions
Installing regexp_parser 2.8.0
Installing rack 2.2.7
Installing timeout 0.3.2
Using io-console 0.6.0
Using rexml 3.2.5
Using rubyzip 2.3.2
Using websocket 1.2.9
Fetching sqlite3 1.6.2 (arm64-darwin)
Using websocket-driver 0.7.5
Fetching nokogiri 1.14.3 (arm64-darwin)
Fetching addressable 2.8.4
Fetching reline 0.3.3
Fetching selenium-webdriver 4.9.0
Installing addressable 2.8.4
Using net-protocol 0.2.1
Fetching rack-test 2.1.0
Using i18n 1.12.0
Using tzinfo 2.0.6
Using sprockets 4.2.0
Using net-imap 0.3.4
Using net-pop 0.1.2
Using net-smtp 0.3.3
Fetching activesupport 7.0.4.3
Installing reline 0.3.3
Fetching mail 2.8.1
Installing rack-test 2.1.0
Fetching irb 1.6.4
Installing sqlite3 1.6.2 (arm64-darwin)
Installing activesupport 7.0.4.3
Installing irb 1.6.4
Fetching debug 1.7.2
Installing nokogiri 1.14.3 (arm64-darwin)
Using globalid 1.1.0
Fetching activemodel 7.0.4.3
Installing debug 1.7.2 with native extensions
Installing activemodel 7.0.4.3
Installing selenium-webdriver 4.9.0
Fetching activejob 7.0.4.3
Installing activejob 7.0.4.3
Installing mail 2.8.1
Fetching activerecord 7.0.4.3
Installing activerecord 7.0.4.3
Using rails-dom-testing 2.0.3
Using xpath 3.2.0
Fetching loofah 2.20.0
Fetching capybara 3.39.0
Fetching webdrivers 5.2.0
Installing loofah 2.20.0
Using rails-html-sanitizer 1.5.0
Fetching actionview 7.0.4.3
Installing webdrivers 5.2.0
Installing capybara 3.39.0
Installing actionview 7.0.4.3
Fetching actionpack 7.0.4.3
Using jbuilder 2.11.5
Fetching puma 5.6.5
Installing actionpack 7.0.4.3
Installing puma 5.6.5 with native extensions
Using sprockets-rails 3.4.2
Fetching actionmailer 7.0.4.3
Fetching actioncable 7.0.4.3
Fetching activestorage 7.0.4.3
Fetching railties 7.0.4.3
Installing actionmailer 7.0.4.3
Installing activestorage 7.0.4.3
Installing actioncable 7.0.4.3
Installing railties 7.0.4.3
Fetching actionmailbox 7.0.4.3
Fetching actiontext 7.0.4.3
Fetching bootsnap 1.16.0
Installing actionmailbox 7.0.4.3
Installing bootsnap 1.16.0 with native extensions
Fetching cssbundling-rails 1.1.2
Fetching stimulus-rails 1.2.1
Fetching turbo-rails 1.4.0
Using web-console 4.2.0
Fetching jsbundling-rails 1.1.1
Installing actiontext 7.0.4.3
Fetching rails 7.0.4.3
Installing cssbundling-rails 1.1.2
Installing stimulus-rails 1.2.1
Installing jsbundling-rails 1.1.1
Installing turbo-rails 1.4.0
Installing rails 7.0.4.3
Bundle complete! 16 Gemfile dependencies, 73 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
         run  bundle binstubs bundler
       rails  javascript:install:esbuild
Compile into app/assets/builds
      create  app/assets/builds
      create  app/assets/builds/.keep
      append  app/assets/config/manifest.js
      append  .gitignore
      append  .gitignore
Add JavaScript include tag in application layout
      insert  app/views/layouts/application.html.erb
Create default entrypoint in app/javascript/application.js
      create  app/javascript
      create  app/javascript/application.js
Add default package.json
      create  package.json
Add default Procfile.dev
      create  Procfile.dev
Ensure foreman is installed
         run  gem install foreman from "."
Fetching foreman-0.87.2.gem
Successfully installed foreman-0.87.2
1 gem installed
Add bin/dev to start foreman
      create  bin/dev
Install esbuild
         run  yarn add esbuild from "."
yarn add v1.22.19
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 2 new dependencies.
info Direct dependencies
└─ esbuild@0.17.18
info All dependencies
├─ @esbuild/darwin-arm64@0.17.18
└─ esbuild@0.17.18
✨  Done in 6.57s.
Add build script
         run  npm pkg set scripts.build="esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets" from "."
         run  yarn build from "."
yarn run v1.22.19
$ esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets

  app/assets/builds/application.js      62b 
  app/assets/builds/application.js.map  93b 

✨  Done in 0.42s.
       rails  turbo:install stimulus:install
Import Turbo
      append  app/javascript/application.js
Install Turbo
         run  yarn add @hotwired/turbo-rails from "."
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 3 new dependencies.
info Direct dependencies
└─ @hotwired/turbo-rails@7.3.0
info All dependencies
├─ @hotwired/turbo-rails@7.3.0
├─ @hotwired/turbo@7.3.0
└─ @rails/actioncable@7.0.4
✨  Done in 3.93s.
Run turbo:install:redis to switch on Redis and use it in development for turbo streams
Create controllers directory
      create  app/javascript/controllers
      create  app/javascript/controllers/index.js
      create  app/javascript/controllers/application.js
      create  app/javascript/controllers/hello_controller.js
Import Stimulus controllers
      append  app/javascript/application.js
Install Stimulus
         run  yarn add @hotwired/stimulus from "."
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ @hotwired/stimulus@3.2.1
info All dependencies
└─ @hotwired/stimulus@3.2.1
✨  Done in 1.67s.
       rails  css:install:bootstrap
Build into app/assets/builds
       exist  app/assets/builds
   identical  app/assets/builds/.keep
File unchanged! The supplied flag value not found!  app/assets/config/manifest.js
Stop linking stylesheets automatically
        gsub  app/assets/config/manifest.js
File unchanged! The supplied flag value not found!  .gitignore
File unchanged! The supplied flag value not found!  .gitignore
Remove app/assets/stylesheets/application.css so build output can take over
      remove  app/assets/stylesheets/application.css
Add stylesheet link tag in application layout
File unchanged! The supplied flag value not found!  app/views/layouts/application.html.erb
      append  Procfile.dev
Add bin/dev to start foreman
   identical  bin/dev
Install Bootstrap with Bootstrap Icons and Popperjs/core
      create  app/assets/stylesheets/application.bootstrap.scss
         run  yarn add sass bootstrap bootstrap-icons @popperjs/core from "."
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 21 new dependencies.
info Direct dependencies
├─ @popperjs/core@2.11.7
├─ bootstrap-icons@1.10.4
├─ bootstrap@5.2.3
└─ sass@1.62.0
info All dependencies
├─ @popperjs/core@2.11.7
├─ anymatch@3.1.3
├─ binary-extensions@2.2.0
├─ bootstrap-icons@1.10.4
├─ bootstrap@5.2.3
├─ braces@3.0.2
├─ chokidar@3.5.3
├─ fill-range@7.0.1
├─ fsevents@2.3.2
├─ glob-parent@5.1.2
├─ immutable@4.3.0
├─ is-binary-path@2.1.0
├─ is-extglob@2.1.1
├─ is-glob@4.0.3
├─ is-number@7.0.0
├─ normalize-path@3.0.0
├─ picomatch@2.3.1
├─ readdirp@3.6.0
├─ sass@1.62.0
├─ source-map-js@1.0.2
└─ to-regex-range@5.0.1
✨  Done in 2.92s.
      insert  config/initializers/assets.rb
Appending Bootstrap JavaScript import to default entry point
      append  app/javascript/application.js
Add build:css script
         run  npm pkg set scripts.build:css="sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules" from "."
         run  yarn build:css from "."
yarn run v1.22.19
$ sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules
✨  Done in 0.94s.
```

ちなみに、—css bootstrapと指定すると-jオプションで他に指定しない限り、javascriptのバンドラーがesbuildで設定されてrails newされます。

記事の本旨とはずれますが、—css tailwindを指定するとimportmapがjavascriptバンドラーに設定されるのでそこは注意が必要です。

- rails g scaffoldしてみる

```bash
rails generate scaffold todo
```

```bash
invoke  active_record
      create    db/migrate/20230425145754_create_todos.rb
      create    app/models/todo.rb
      invoke    test_unit
      create      test/models/todo_test.rb
      create      test/fixtures/todos.yml
      invoke  resource_route
       route    resources :todos
      invoke  scaffold_controller
      create    app/controllers/todos_controller.rb
      invoke    erb
      create      app/views/todos
      create      app/views/todos/index.html.erb
      create      app/views/todos/edit.html.erb
      create      app/views/todos/show.html.erb
      create      app/views/todos/new.html.erb
      create      app/views/todos/_form.html.erb
      create      app/views/todos/_todo.html.erb
      invoke    resource_route
      invoke    test_unit
      create      test/controllers/todos_controller_test.rb
      create      test/system/todos_test.rb
      invoke    helper
      create      app/helpers/todos_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/todos/index.json.jbuilder
      create      app/views/todos/show.json.jbuilder
      create      app/views/todos/_todo.json.jbuilder
```

- bootstrap公式ページのsample componentを使ってjsとcssの動作確認をする。
    - まずはヘッダーのパーシャルを作成する。
    
    ```bash
    touch app/views/layouts/_header.html.erb
    ```
    
    ```ruby
    # 以下のコードを追加 https://getbootstrap.jp/docs/5.0/components/navbar/
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar w/ text</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarText">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Features</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Pricing</a>
            </li>
          </ul>
          <span class="navbar-text">
            Navbar text with an inline element
          </span>
        </div>
      </div>
    </nav>
    ```
    
    - application.html.erbにヘッダーを表示するためのrenderメソッドを追加する。
    
    ```ruby
    <!DOCTYPE html>
    <html>
      <head>
        <title>Myapp</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <%= csrf_meta_tags %>
        <%= csp_meta_tag %>
    
        <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
        <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
      </head>
    
      <body>
        <%= render 'layouts/header'%> # これを追加
        <%= yield %>
      </body>
    </html>
    ```
    
    - jsとcssをバンドルして（yarnがインストールされている必要があります）ビルドする。
    
    ```bash
    bin/dev # rails serverと同じようなコマンド
    ```
    
- localhost:3000にアクセスしてヘッダーがうまく表示されているか確認する
    
    ヘッダーにカーソルを当てたりして色が変わればCSSがうまく動いている。
    

![スクリーンショット 2023-04-26 0.40.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092884/59f010ae-f4f2-cc5f-25f1-df0b967e8284.png)

デベロッパーツールでレスポンシブモードを使ってトグルが動くか確認する。

![スクリーンショット 2023-04-26 0.41.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092884/9a9872d7-aa39-f160-acef-63f117bc4eb6.png)


### 最後に

筆者はあくまで初学者のため、他のもっと初学者の方向けに記事を書いています。間違え等あると思いますので生暖かい目で見ていただけると幸いです。ちなみに間違いがあればお気軽にコメントしてくださると助かります。

### 参考にした記事

[https://qiita.com/jnchito/items/5c41a7031404c313da1f](https://qiita.com/jnchito/items/5c41a7031404c313da1f)

[https://railsguides.jp/working_with_javascript_in_rails.html](https://railsguides.jp/working_with_javascript_in_rails.html)