Rails 6/7にjQueryとBootstrap 5を入れる（その1）

RailsにjQueryとBootstrap 5を入れる方法を調べながら、Rails 7のCSSとJavaScriptの新機能を調べてみました。次のようなコードを動かすのが目標です。BootstrapのjQueryプラグインを使うものです。

```javascript
$('[data-bs-toggle="tooltip"]').tooltip()
```

あるいは、次のようにjQueryプラグインを使わない方法でもよしとします。

```javascript
$('[data-bs-toggle="tooltip"]').each((idx, elm) => {
  new bootstrap.Tooltip(elm)
})
```


## Webpacker（Rails 6.1）

まず、Rails 7の新機能を使わず、Rails 6にWebpackerで導入する方法です。yarnでBootstrap 5、Popper 2、jQueryを入れます。

```
% yarn add bootstrap @popperjs/core jquery 
```

BootstrapのCSS部分を導入します。app/javascript の下に stylesheets というフォルダを作り、application.scss を作成して、次のように記述します。

```scss:app/javascript/stylesheets/application.scss
@import "~bootstrap/scss/bootstrap";
```

これを packs/application.js で import します。

```javascript:app/javascript/packs/application.js
import "../stylesheets/application"
```

レイアウトに stylesheet_pack_tag を加えます。
 
```erb:app/views/layouts/application.html.erb
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
```

BootstrapのJavaScript部分を導入します。[Rails 6にjQueryとBootstrapを入れる](https://qiita.com/kazutosato/items/d47b7705ee545de4cb1a) で紹介した ProvidePlugin を使う方法は、Bootstrap 5では効きません。

そこで、グローバル変数として使えるように、単純に window オブジェクトに $ と jQuery を加えることにします。ついでに bootstrap もグローバル変数として使えるようにしておきます。
 
```javascript:app/javascript/packs/application.js
import jquery from "jquery"
window.$ = window.jQuery = jquery
import * as bootstrap from "bootstrap"
window.bootstrap = bootstrap
import "../stylesheets/application"
```

## esbuild（Rails 7）

※ここから先を読まれる方は、別記事 [JavaScript BundlingとCSS Bundlingのしくみ](https://qiita.com/kazutosato/items/1ae1cf0ec380a75d4dc4) を先にごらんください。

Rails 7とesbuildの組み合わせを試します。次のコマンドでRailsアプリケーションを作成します。yarnによって、esbuild、Bootstrap、Popper、およびsassが node_module 下にインストールされます。

```
% rails new app -j esbuild --css bootstrap
```

jQueryをyarnで入れておきます。

```
% yarn add jquery
```

BootstrapのCSSは、application.bootstrap.scss で取り込まれます。自分のCSSを追加するには、このscssに追加するか、このscssにインポートします。なお、[別記事]()に書いたように、このファイル名は好きに変えられます。

```app/assets/styleseets/application.bootstrap.scss
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
```

JavaScript側は、app/javascript/packs ではなく、app/javascript ディレクトリのすぐ下にエントリーポイントとなる application.js ができます。

上記のWebpacker（Rails 6.1）と同じコードを試します。すると、BootstapのjQueryプラグインが効きません。

```javascript:app/javascript/application.js
import jquery from "jquery"
window.$ = window.jQuery = jquery
import * as bootstrap from "bootstrap"
window.bootstrap = bootstrap
```

これは、`import * as bootstrap from "bootstrap"` が `window.$ = window.jQuery = jquery` よりも先に実行されるためです。Bootstrapが初期化されるときに `window.jQuery` が存在しないのでjQueryプラグインが設定されない、というわけです。

require を使えば、Bootstrapの初期化を後にしてjQueryプラグインが使えるようになります。

```javascript:app/javascript/application.js
import jquery from "jquery"
window.$ = window.jQuery = jquery
window.bootstrap = require("bootstrap")
```

「今どきのJavaScriptでグローバル変数はダサい」という方は、各JSファイルにimportを書きましょう。

```javascript
import $ from 'jquery'
import { Tooltip } from "bootstrap"

$('[data-bs-toggle="tooltip"]').each((idx, elm) => {
  new Tooltip(elm)
})
```

package.jsonの中身を確認しておきましょう。コマンド`yarn build`を実行すると、esbuildコマンドでJSファイルが変換されます。コマンド`yarn build:css`では、sassコマンドでCSSファイルが変換されます。

```json:package.json
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds",
    "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
  }
```

binディレクトリの下のdevコマンドでRailsを起動します。JSやCSSのファイルが更新されるごとに自動的にファイルが変換されるようになります。

```
% bin/dev
```

## webpack（Rails 7）

Rails 7とwebpackの組み合わせを試します。Webpackerなしで[webpack](https://webpack.js.org/)を使うものです。次のコマンドでRailsアプリケーションを作成します。yarnによって、webpack、Bootstrap、Popper、sassが node_module 下にインストールされます。

```
% rails new app -j webpack --css bootstrap
```

yarnでjQueryを入れておきます。

```
% yarn add jquery
```

esbuildの場合と同じく、BootstrapのCSSは、application.bootstrap.scss で取り込まれます。

```app/assets/styleseets/application.bootstrap.scss
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
```

esbuildの場合と同じく、BootstrapのjQueryプラグインを使うには、jQueryをグローバル変数にしたあとでrequireを使ってBootstrapを取り込む必要があります（同じwebpackなのにWebpackerを使った場合と挙動が変わる原因は不明）。

```javascript:app/javascript/application.js
import jquery from "jquery"
window.$ = window.jQuery = jquery
window.bootstrap = require("bootstrap")
```

package.jsonの中身を見てみましょう。コマンド`yarn build`を実行すると、webpackコマンドでJSファイルが変換されます。webpackの設定ファイルはアプリケーションのルートにできる webpack.config.js です。

```json:package.json
  "scripts": {
    "build": "webpack --config webpack.config.js",
    "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
  }
```

webpack.config.js に少し手を加えます。オブジェクト`process.env`で環境変数`RAILS_ENV`と`NODE_ENV`が使えるようにしておいて、modeオプションに`production`か`development`を指定します。こうしておくと、JSがminify（圧縮）されるのはproduction環境の場合だけになります。

エントリーポイントとなるJSファイルの名前や場所を変えたいときは、`application: "./app/javascript/application.js"`の行を修正します。

```javascript:webpack.config.js
const path    = require("path")
const webpack = require("webpack")

process.env.RAILS_ENV = process.env.RAILS_ENV || 'development'
process.env.NODE_ENV = process.env.NODE_ENV || process.env.RAILS_ENV

module.exports = {
  mode: ['development', 'production'].includes(process.env.NODE_ENV) ? process.env.NODE_ENV : 'development',
  devtool: "source-map",
  entry: {
    application: "./app/javascript/application.js"
  },
  output: {
    filename: "[name].js",
    sourceMapFilename: "[name].js.map",
    path: path.resolve(__dirname, "app/assets/builds"),
  },
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    })
  ]
}
```

binディレクトリの下のdevコマンドでRailsを起動します。JSやCSSのファイルが更新されるごとに自動的にファイルが変換されるようになります。

```
% bin/dev
```

## rollup（Rails 7）

rollupを使った場合はどうもうまくいきませんでした。jQueryがインポートできない、Popperが動かない。あきらめるとこととします。

## importmap（Rails 7）

importmapを使った例は、「Rails 6/7にjQueryとBootstrap 5を入れる（その2）」に書く予定です。

## esbuild/webpack（Rails 6.1）

Rails 6を使いつつRails 7のJavaScriptに移行するつもりなら、Rails 6にJavaScript BundlingとCSS Bundlingを導入して、esbuildやwebpackを使ってみるとよいでしょう。

まず、JavaScript BundlingとCSS BundlingをBundlerで入れます。

```ruby:Gemfile
gem "jsbundling-rails"
gem "cssbundling-rails"
```

```
% bundle install
```

次のコマンドでインストールと初期設定ができます。webpackの場合は`bin/rails javascript:install:webpack`とします。

```
% bin/rails javascript:install:esbuild
% bin/rails css:install:bootstrap
```

yarnでjQueryを入れておきます。

```
% yarn add jquery
```

CSS Bundlingのエントリーポイントとなる application.bootstrap.scss を確認します。

```app/assets/styleseets/application.bootstrap.scss
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
```

JavaScript Bundlingのエントリーポイントとなる application.js でjQueryとBootstrapをインポートします。上記の「esbuild（Rails7）」での説明をごらんください。

```javascript:app/javascript/application.js
import jquery from "jquery"
window.$ = window.jQuery = jquery
window.bootstrap = require("bootstrap")
```

package.jsonに追加されたコマンドを確認します。内容については、上記の「esbuild（Rails7）」での説明をごらんください。また、webpackの場合は上記の「webpack（Rails 7）」と同様に、webpack.config.js で設定をカスタマイズできます。

```json:package.json
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds",
    "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
  }
```

binディレクトリの下のdevコマンドでRailsを起動します。
```
% bin/dev
```

既存のアプリケーションで作ったJavaScriptやCSSを生かしつつ、JavaScript BundlingとCSS Bundlingを使うには、[既存のJavaScript/CSSと同居させる](https://qiita.com/kazutosato/items/1ae1cf0ec380a75d4dc4#%E6%97%A2%E5%AD%98%E3%81%AEjavascriptcss%E3%81%A8%E5%90%8C%E5%B1%85%E3%81%95%E3%81%9B%E3%82%8B)を参照してください。